12

我有两个进程(参见示例代码),每个进程都尝试访问一个 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 时,它按预期工作。

4

3 回答 3

9

您提到的两个操作系统都是基于 Unix/Linux 的,因此实现了相同的fork()API。Afork()完全复制了进程对象,以及它的内存、加载的代码、打开的文件描述符和线程。此外,在第一次内存写入操作之前,新进程通常在内核中共享相同的进程对象。这基本上意味着本地数据结构也与线程局部变量一起被复制到新进程中。因此,您仍然拥有相同的数据结构并且l.x仍然被定义。

要为新进程重置数据结构,我建议进程启动函数首先调用一些清除方法。例如,您可以存储父进程 pidprocess_id = os.getpid()并使用

if process_id != os.getpid(): 
   clear_local_data()

在子进程main函数中。

于 2011-09-02T15:44:16.983 回答
3

因为正如其文档threading.local中明确描述的那样,线程的技巧,而不是进程的技巧:

对于不同的线程,实例的值会有所不同。

与流程无关。

以及来自多处理文档的报价:

笔记

多处理不包含 threading.active_count()、threading.enumerate()、threading.settrace()、threading.setprofile()、threading.Timer 或threading.local的类似物。

于 2011-09-02T15:33:22.390 回答
1

现在pypi 上有一个multiprocessing-utils ( githubthreading.local() ) 库,它的多处理安全版本可以通过 pip 安装。

它的工作原理是包装一个标准threading.local()并检查 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"

完全披露:我刚刚创建了这个模块

于 2017-10-20T12:57:27.103 回答