我正在构建一个 Flask 应用程序,由两部分组成:
这两部分之间的桥梁是TaskExecutor的全局对象。
任务执行器.py
def execute(task, reporter):
conn = ResourceConnection(...)
with conn as c:
for f in task:
func, args = f
func(c, **args)
reporter.report(...) # updates state of execution
class TaskExecutor:
def __init__(self):
self._process = Process()
self.reporter = Reporter() # some variables
def _is_alive(self): # checks if the process is alive
...
def _graceful_term(self): # stops the process
...
def start(self, task):
if self._is_alive():
self._graceful_term()
self._process = Process(target=executor,
args=(task, self.reporter))
self._process.start()
def stop(self):
...
控制bp.py
control_bp = Blueprint('control', __name__, url_prefix='/control')
task_executor = TaskExecutor()
@control_bp.route('/demo', methods=["POST"])
def run_demo():
task_executor.start([demo_func, {"a": 42}])
return ""
@control_bp.route('/state')
def get_state():
return task_executor.reporter.to_json()
现在,在 Flask 开发环境中,一切正常。
我开始研究 Gunicorn,似乎不清楚我的“执行器部分”中是否只有一个任务会同时运行以及它是否能正常工作。 Gunicorn 似乎会产生子进程(工作者),它们将具有共享内存,这些内存仅共享读取,而不共享写入或执行。我错了,还是我需要以某种方式修改桥梁? Gunicorn 如何使用共享对象生成工人?
如果这很重要,我的应用程序将同时只有 1-2 个用户。
Gunicorn 似乎会产生子进程(工作者),它们将具有共享内存,仅共享读取,而不共享写入或执行。
其实不然。 Gunicorn 主进程生成的工作进程将拥有自己的内存副本,供工作进程读写。
Gunicorn 如何使用共享对象生成工人?
gunicorn 的主进程与它的工作进程共享的唯一东西是打开的文件描述符和锁。
Gunicorn 依赖于 Python 的
os.fork()
方法来生成称为工作进程的子进程。 os.fork() 仅适用于基于 POSIX 的系统。所以基本上 os.fork
将拥有您用于 fork 操作的操作系统平台的行为。 Windows不支持fork,MacOS的fork有bug,所以你只剩下linux了。
在基于 Unix 的系统上,
fork
与 CoW(写入时复制)机制一起发生。其中指出,从父进程派生的子进程将共享其父进程的内存以进行读取操作。每当子进程尝试在共享内存上写入时,操作系统都会将子进程尝试写入的内存块复制到子进程自己的内存空间中。所以不能对共享内存进行写操作。
在Python中,如果GC(垃圾收集器)处于活动状态,即使是读操作也可以被视为写操作。因为,每当您尝试在 Python 中读取任何变量或对象时,它的引用计数都会增加 1。Python 对象在 CPython 中以
c
语言存储为 pyobj 结构体。相同的结构包含引用计数。因此,当您增加引用计数时,结构就会更新,这是操作系统级别的写入操作。