我正在尝试在进程之间共享锁。我知道共享锁的方法是将其作为参数传递给目标函数。但是,我发现即使是下面的方法也有效。我无法理解进程共享此锁的方式。谁能解释一下?
import multiprocessing as mp
import time
class SampleClass:
def __init__(self):
self.lock = mp.Lock()
self.jobs = []
self.total_jobs = 10
def test_run(self):
for i in range(self.total_jobs):
p = mp.Process(target=self.run_job, args=(i,))
p.start()
self.jobs.append(p)
for p in self.jobs:
p.join()
def run_job(self, i):
with self.lock:
print('Sleeping in process {}'.format(i))
time.sleep(5)
if __name__ == '__main__':
t = SampleClass()
t.test_run()
在Windows(你说你正在使用的(上,这类事情总是简化为关于multiprocessing
如何使用pickle
的细节,因为在Windows上跨越进程边界的所有Python数据都是通过在发送端进行酸洗(并在接收端取消酸洗(来实现的。
我最好的建议是避免做一开始就引发此类问题的事情;-( 例如,您展示的代码在 Python 2 下的 Windows 上会爆炸,如果您使用multiprocessing.Pool
方法而不是multiprocessing.Process
,也会在 Python 3 下爆炸。
这不仅仅是锁,简单地试图在Python 2中腌制绑定方法(如self.run_job
(会爆炸。 想想吧。 您正在跨越进程边界,并且接收端没有与self
对应的对象。self.run_job
应该在接收端绑定到什么对象?
在 Python 3 中,酸洗self.run_job
也会腌制self
对象的副本。 这就是答案:与self
对应的SampleClass
对象是由接收端的魔法创建的。 清澈如泥。t
整个州都是腌制的,包括t.lock
。 这就是它"有效"的原因。
有关更多实现详细信息,请参阅此处:
为什么我可以将实例方法传递给多处理。进程,但不是多处理。池?
从长远来看,如果你坚持那些明显打算工作的东西,你将遭受最少的谜团:传递模块全局可调用对象(既不传递,例如,实例方法也不是本地函数(,并显式传递multiprocessing
数据对象(无论是Lock
、Queue
、manager.list
等的实例(。
在Unix操作系统上,新进程是通过fork
原语创建的。
fork
基元的工作原理是克隆父进程内存地址空间,将其分配给子进程。子项将拥有父项内存的副本以及文件描述符和共享对象的副本。
这意味着,当你调用fork时,如果父级打开了一个文件,那么子级也会拥有它。这同样适用于共享对象,例如管道、插座等......
在Unix+CPython中,Locks
是通过sem_open
原语实现的,该原语旨在在分叉进程时共享。
我通常建议不要混合并发(特别是多处理(和OOP,因为它经常导致这种误解。
编辑:
刚才看到你正在使用Windows。蒂姆·彼得斯给出了正确的答案。为了抽象起见,Python 试图通过其 API 提供独立于操作系统的行为。调用实例方法时,它将选择对象并通过管道发送。从而提供与Unix类似的行为。
我建议您阅读多处理的编程指南。您的问题在第一点中得到了特别解决:
避免共享状态
应尽可能避免在进程之间转移大量数据。
最好坚持使用队列或管道进行进程之间的通信,而不是使用较低级别的同步原语。