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

Python os.fork()之后的共享对象,python,unix,multiprocessing,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, qu

在使用许多进程与数据库交互时,我遇到了一些奇怪的应用程序行为。我正在使用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
查询执行器
在实例化后不会被修改

最初只有一个进程,它会不时地分叉几次(
os.fork()
)。新流程是执行某些任务然后退出的工人。每个工作者调用
query\u executor()
以执行SQL查询

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

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

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

提前谢谢


Grzegorz

问题的根源在于
fork()
只是创建了一个进程的完全独立的副本,但这两个进程都没有。这就是为什么MySQL服务器写入的任何数据都可能[正确地]只从单个进程读取,如果两个进程试图发出请求并读取响应,那么它们很可能会相互干扰。这与“多线程”无关,因为在多线程的情况下,只有一个进程执行很少的线程,它们共享数据并可能进行协调


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

问题的根源在于
fork()
只是创建了一个进程的完全独立的副本,而这两个进程除外。这就是为什么MySQL服务器写入的任何数据都可能[正确地]只从单个进程读取,如果两个进程试图发出请求并读取响应,那么它们很可能会相互干扰。这与“多线程”无关,因为在多线程的情况下,只有一个进程执行很少的线程,它们共享数据并可能进行协调


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

如果我在
QueryExecutor
字段
multiprocessing.Lock
中声明,并使用它同步方法
execute\u query
的使用,这是一个好主意吗?所有分叉的进程都会看到相同的锁吗?在
multiprocessing
docs中,锁作为参数
process(target=f,args=(lock,num)).start()
传递给新进程,但我不能这样做,因为
os.fork()
是在外部库中完成的。很抱歉,过早地点击了。如果进程同时写入流,并且您希望清楚地将一个“数据包”(例如,一行输出)与另一个“数据包”分开,则可能多处理.Lock会有所帮助。但是,如果您处理双向通信,比如SQL请求/响应会话,那么它不是一个解决方案,因为响应不会与请求一对一地连接。因此,我建议在子进程中强制关闭然后重新打开QueryExecutor连接。好的,我理解,但如果在执行时持有锁,会有什么问题:
cur=self.\u conn.cursor();当前执行(查询);cur.close();返回结果
?啊,这样可能会起作用。但是要准备好,您的进程大部分时间都会互相等待。如果我在
QueryExecutor
字段
multiprocessing.Lock
中声明,并使用它来同步方法
execute\u query
的使用,这是一个好主意吗?所有分叉的进程都会看到相同的锁吗?在
multiprocessing
docs中,锁作为参数
process(target=f,args=(lock,num)).start()
传递给新进程,但我不能这样做,因为
os.fork()
是在外部库中完成的。很抱歉,过早地点击了。如果进程同时写入流,并且您希望清楚地将一个“数据包”(例如,一行输出)与另一个“数据包”分开,则可能多处理.Lock会有所帮助。但是,如果您处理双向通信,比如SQL请求/响应会话,那么它不是一个解决方案,因为响应不会与请求一对一地连接。因此,我建议在子进程中强制关闭然后重新打开QueryExecutor连接。好的,我理解,但如果在执行时持有锁,会有什么问题:
cur=self.\u conn.cursor();当前执行(查询);cur.close();返回结果
?啊,这样可能会起作用。但是要准备好,您的流程大部分时间都会互相等待。