在下面的程序中,创建了两个进程。每个接收它自己的 multiprocessing.Value
对象。不知何故,这两个过程仍然相互影响。为什么?
我预计这两个进程会死锁:第一个进程可以将计数器递增一次(最初为 0),但随后应该等待它再次变为偶数。第二个进程永远不能增加它,因为它最初是 0 并且永远不会改变。不知何故,这种死锁并没有发生,两个进程都继续运行。
import multiprocessing
import time
def inc_even(counter):
print(f"inc_even started. {hash(counter)=}")
while True:
if counter.value % 2 == 0:
counter.value += 1
print("inc_even incremented counter")
else:
time.sleep(1)
def inc_odd(counter):
print(f"inc_odd started. {hash(counter)=}")
while True:
if counter.value % 2 == 1:
counter.value += 1
print("inc_odd incremented counter")
else:
time.sleep(1)
multiprocessing.Process(
target=inc_even,
kwargs={
"counter": multiprocessing.Value("i", 0),
},
).start()
multiprocessing.Process(
target=inc_odd,
kwargs={
"counter": multiprocessing.Value("i", 0),
},
).start()
输出:
inc_even started. hash(counter)=8786024404161
inc_even incremented counter
inc_odd started. hash(counter)=8786024404157
inc_odd incremented counter
inc_even incremented counter
inc_odd incremented counter
...
有趣的是,如果我将其更改为首先创建两个保存两个计数器的变量,则会发生死锁:
counterA = multiprocessing.Value("i", 0)
counterB = multiprocessing.Value("i", 0)
multiprocessing.Process(
target=inc_even,
kwargs={
"counter": counterA,
},
).start()
multiprocessing.Process(
target=inc_odd,
kwargs={
"counter": counterB,
},
).start()
输出:
inc_even started. hash(counter)=8765172881357
inc_even incremented counter
inc_odd started. hash(counter)=8765172756097
编辑:如果我用某些自定义类替换 multiprocessing.Value
对象,则不会发生这种情况:
class CustomCounter:
def __init__(self) -> None:
self.value = 0
multiprocessing.Process(
target=inc_even,
kwargs={
"counter": CustomCounter(),
},
).start()
multiprocessing.Process(
target=inc_odd,
kwargs={
"counter": CustomCounter(),
},
).start()
这正如预期的那样陷入僵局。所以它一定是由 multiprocessing.Value
引起的,而不仅仅是一般的多处理。
最佳答案
第一个值在父进程中被回收,导致第二个值使用相同的共享内存进行分配。
根据文档,您的代码应该可以工作。下"Explicitly pass resources to child processes" ,文档说
Apart from making the code (potentially) compatible with Windows and the other start methods this also ensures that as long as the child process is still alive the object will not be garbage collected in the parent process. This might be important if some resource is freed when the object is garbage collected in the parent process.
不幸的是,文档与实现不匹配。实际执行explicitly deletes BaseProcess.start
内应该阻止您的值被回收的引用:
del self._target, self._args, self._kwargs
这意味着您的代码仅在您自己保存对 Value 实例的引用时才有效,就像您在使用 counterA
和 counterB
变量的版本中所做的那样。
文档和实现之间的不匹配可能应该在 CPython 上报告 issue tracker .
关于python - 为什么不同的 multiprocessing.Value 对象指向相同的值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77269319/