os.fork() 之后的共享对象

Grz*_*zes 1 python unix multiprocessing

我在使用许多进程与数据库交互时遇到了一些奇怪的应用程序行为。我正在使用Linux。

我有自己的实现,QueryExecutor它在其生命周期内使用单个连接:

class QueryExecutor(object):
    def __init__(self, db_conf):
        self._db_config = db_conf
        self._conn = self._get_connection()

    def execute_query(self, query):
        # some code
    # some more code

def query_executor():
    global _QUERY_EXECUTOR
    if _QUERY_EXECUTOR is None:
        _QUERY_EXECUTOR = QueryExecutor(some_db_config)
    return _QUERY_EXECUTOR
Run Code Online (Sandbox Code Playgroud)

Query Executor实例化后永远不会被修改。

最初只有一个进程,它时不时会 fork ( os.fork()) 几次。新进程是执行一些任务然后退出的工作进程。每个工作人员调用query_executor()都能够执行 SQL 查询。

我发现sql查询经常返回错误的结果(似乎有时sql查询结果返回到错误的进程)。唯一合理的解释是所有进程共享相同的sql连接(根据MySQLdb文档:threadsafety = 1线程可以共享模块,但不能共享连接)。

我想知道是哪种操作系统机制导致了这种情况。据我所知,在Linux上,当进程分叉时,父进程的页面不会被复制给子进程,它们由两个进程共享,直到其中一个进程尝试修改某些页面(写入时复制)。正如我之前提到的,QueryExecutor对象在创建后保持不变。QueryExecutor我想这就是所有进程使用相同实例并因此使用相同 sql 连接的原因。

我是对的还是我错过了什么?你有什么建议吗?

提前致谢!

格热戈日

use*_*253 5

问题的根源在于,fork()只是创建了一个进程的精确独立副本,但这两个进程共享打开的文件、套接字和管道。这就是为什么 MySQL 服务器写入的任何数据可能只能从单个进程中[正确地]读取,如果两个进程尝试发出请求并读取响应,那么它们很可能会扰乱彼此的工作。这与“多线程”无关,因为在多线程的情况下,有一个进程只有很少的执行线程,它们共享数据并可能进行协调。

正确的使用方法fork()是在进程的所有副本(除了一个副本)中分叉所有类似文件句柄的对象后立即关闭(或重新打开),或者至少避免从多个进程中使用它们。