Python的multiprocessing与threading.local不兼容?
我有两个进程(见下面的示例代码),它们都试图访问一个叫做 threading.local 的对象。我本以为下面的代码会打印出 "a" 和 "b"(顺序可以是任意的)。结果却打印出了 "a" 和 "a"。我该如何优雅且稳妥地在启动全新的进程时重置这个 threading.local 对象呢?
import threading
import multiprocessing
l = threading.local()
l.x = 'a'
def f():
print getattr(l, 'x', 'b')
multiprocessing.Process(target=f).start()
f()
补充说明:作为参考,当我使用 threading.Thread 而不是 multiprocessing.Process 时,结果是如我所预期的那样。
3 个回答
3
现在有一个叫做 multiprocessing-utils 的库,可以在 pypi 上找到,里面有一个安全的版本的 threading.local()
,可以通过 pip 安装。
这个库的工作原理是,它会把标准的 threading.local()
包装起来,并检查自上次使用以来进程ID(PID)是否没有改变(具体可以参考 这里 @immortal 的回答)。
使用方法和 threading.local()
一模一样:
l = multiprocessing_utils.local()
l.x = 'a'
def f():
print getattr(l, 'x', 'b')
f() # prints "a"
threading.Thread(target=f).start() # prints "b"
multiprocessing.Process(target=f).start() # prints "b"
需要说明的是:这个模块是我刚刚创建的。
9
你提到的这两个操作系统都是基于Unix/Linux的,所以它们使用的是相同的fork()
接口。fork()
这个操作会完全复制一个进程,包括它的内存、加载的代码、打开的文件描述符和线程。而且,新创建的进程通常会在内核中共享同一个进程对象,直到第一次写入内存。这基本上意味着,局部数据结构也会被复制到新进程中,包括线程局部变量。因此,你仍然拥有相同的数据结构,l.x
依然是定义好的。
为了重置新进程的数据结构,我建议在进程启动的函数中,先调用一些清理的方法。比如,你可以用process_id = os.getpid()
来存储父进程的进程ID,然后在
if process_id != os.getpid():
clear_local_data()
的子进程主函数中使用它。