我有两个进程(参见示例代码),每个进程都尝试访问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时,它按预期工作。
答案 0 :(得分:8)
您提到的两种操作系统都是基于Unix / Linux的,因此实现了相同的fork()
API。
fork()
完全复制了进程对象及其内存,加载的代码,打开的文件描述符和线程。此外,新进程通常在内核中共享相同的进程对象,直到第一次内存写入操作。这基本上意味着本地数据结构也与线程局部变量一起被复制到新进程中。因此,您仍然拥有相同的数据结构,并且仍然定义了l.x
。
要重置新进程的数据结构,我建议使用进程启动功能首先调用某些清除方法。例如,您可以使用process_id = os.getpid()
存储父进程pid并使用
if process_id != os.getpid():
clear_local_data()
在子进程主函数中。
答案 1 :(得分:3)
因为threading.local
为线程而不是进程提供了技巧,如documentation中所述:
对于不同的线程,实例的值会有所不同。
没有关于流程的内容。
来自多处理doc的引用:
请注意
多处理不包含threading.active_count()的类似物, threading.enumerate(),threading.settrace(),threading.setprofile(), threading.Timer,或 threading.local 。
答案 2 :(得分:0)
现在pypi上有一个multiprocessing-utils(github)库,其中包含一个可以安装pip的多处理安全版threading.local()
。
它通过包装标准threading.local()
并检查自上次使用后PID没有发生变化(根据@immortal的答案here)。
使用它与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"
完全披露:我刚刚创建了这个模块