其他答案已经提供了答案,即除非提供适当的error_callback
参数,否则apply_async
会默默失败。我仍然认为OP提到的另一点是有效的——官方文档确实展示了将multiprocessing.Lock
作为函数参数传递的情况。事实上,在Programming guidelines的“显式地向子进程传递资源”这个小节中,推荐将multiprocessing.Lock
对象作为函数参数传递,而不是全局变量。我写了很多代码,其中我将multiprocessing.Lock
作为一个参数传递给子进程,一切都按预期工作。
那么问题出在哪里呢?
我首先调查了multiprocessing.Lock
是否可被pickle。在Python 3、MacOS+CPython中,尝试pickle multiprocessing.Lock
会产生其他人遇到的熟悉的RuntimeError
错误提示。
>>> pickle.dumps(multiprocessing.Lock())
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-7-66dfe1355652> in <module>
----> 1 pickle.dumps(multiprocessing.Lock())
/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/synchronize.py in __getstate__(self)
99
100 def __getstate__(self):
--> 101 context.assert_spawning(self)
102 sl = self._semlock
103 if sys.platform == 'win32':
/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/context.py in assert_spawning(obj)
354 raise RuntimeError(
355 '%s objects should only be shared between processes'
--> 356 ' through inheritance' % type(obj).__name__
357 )
RuntimeError: Lock objects should only be shared between processes through inheritance
对我而言,这证实了确实不可被pickle化。
旁白开始
但是,同一个锁仍需要在两个或多个Python进程之间共享,这些进程将具有自己的潜在不同的地址空间(例如,在使用"spawn"或"forkserver"作为启动方法时)。必须要做一些特殊的事情才能发送Lock到跨进程。这其他StackOverflow帖子似乎表明,在Unix系统中,可能是通过由操作系统本身(在python外部)支持的命名信号量来实现的。然后,两个或多个Python进程可以链接到在一个位置有效地驻留在两个Python进程之外的同一个锁。也可能会有共享内存的实现。
旁白结束
我们是否可以将对象作为参数传递?
经过更多实验和阅读后,看起来区别在于和之间。 允许您将作为参数传递,但则不允许。以下是一个有效的示例:
import multiprocessing
import time
from multiprocessing import Process, Lock
def task(n: int, lock):
with lock:
print(f'n={n}')
time.sleep(0.25)
if __name__ == '__main__':
multiprocessing.set_start_method('forkserver')
lock = Lock()
processes = [Process(target=task, args=(i, lock)) for i in range(20)]
for process in processes:
process.start()
for process in processes:
process.join()
请注意,在“安全导入主模块”子部分中提到的使用
__name__ == '__main__'
是必要的,详见
编程指南。
multiprocessing.Pool
似乎使用
queue.SimpleQueue
将每个任务放入队列中,这就是发生 pickling 的地方。很可能,
multiprocessing.Process
没有使用 pickling(或者在使用某个特殊版本的 pickling)。