Posted on:
Last modified:
按照 posix 的规范来说,fork 之后的进程中,应该只有调用 fork 的那个线程。但是实际上,所有 线程都在,只是除了调用 fork 的线程以外,其他线程都被冻结了,并不会执行。一般来说,在 fork 之后继续执行 exec* 的话,是不会有什么大问题的。
fork 复制的是整个进程的空间,锁也会被复制。
可能会引起问题的地方:如果在 fork 之前的进程中有锁,而且是被其他的线程持有的,那么 fork 之后的进程中,这个锁永远不会有人来释放了,导致新的进程中的线程永远处于等待锁的状态。
fork 还会把所有打开的文件,socket 等描述符都复制一份。
即使在单线程环境中,这也可能引起问题,因为两个进程可能开始争抢同一个资源。所以合理的方式 总是在新的子进程中打开资源(比如数据库),而不是打开资源之后再 fork 出子进程。
在 Python 中,multiprocessing 默认使用的是 fork, 但是还好我们可以选择使用 spawn. 参见参考 文献 3 和 4.
在 web server 和 rpc 中,多进程模式下 fork 是何时执行的呢?使用的 fork 还是 spawn? 全局 变量尤其是数据库链接会不会每次初始化?
在 RPC 服务中,有一种常见的模式,我们创建了一个 Handler 类,在脚本里面直接实例化了一个类, 在这个类的构造函数中初始化了数据库等资源的链接,然后把 my_hanlder.handle 函数交给框架来 作为入口函数,这样合理吗?
Google 的 gRPC 在这方面显然是一个反面的例子,本身他不是一个 Python 的库,内部使用了各种 奇奇怪怪的技术,在 Python 中使用的时候就遇到了各种问题,以至于没有一个很好的多进程模式。
在 web server 中,uwsgi/gunicorn 这些 process runner 又是怎样处理的呢?
不管怎样,这两个都使用了非常简洁的 prefork 模型,而且在文档中明确说明了会采用那种模式, 非常优秀。
© 2016-2022 Yifei Kong. Powered by ynotes
All contents are under the CC-BY-NC-SA license, if not otherwise specified.
Opinions expressed here are solely my own and do not express the views or opinions of my employer.
友情链接: MySQL 教程站