Python:为什么多进程锁在这里的进程之间共享?

时间:2017-07-11 00:32:58

标签: python python-3.x locking multiprocessing shared-variable

我正在尝试在进程之间共享一个锁。我知道共享锁的方法是将它作为参数传递给目标函数。但是我发现即使是下面的方法也是有效的。我无法理解进程共享此锁的方式。有人可以解释一下吗?

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()

2 个答案:

答案 0 :(得分:4)

在Windows上(你说你正在使用),这些事情总是会减少multiprocessing如何与pickle一起使用的细节,因为所有跨越Windows上的流程边界的Python数据都是通过在发送端进行酸洗(并在接收端进行取消)。

我最好的建议是避免做一些提出这些问题的事情开始;-)例如,你展示的代码在Python 2下在Windows上爆炸,如果使用{{1 }}方法而不是multiprocessing.Pool

这不只是锁定,只是试图挑选绑定方法(如multiprocessing.Process)在Python 2中爆炸。想一想。您正在越过进程边界,并且不是接收端上与self.run_job对应的对象。 {(1}}应该在接收端绑定哪个对象?

在Python 3中,pickle self 挑选self.run_job对象的副本。这就是答案:对应于self.run_job的{​​{1}}对象是由接收端的魔法创建的。清除泥土。 self整个州被腌制,包括SampleClass。这就是它“有效”的原因。

有关更多实施细节,请参阅此处:

Why can I pass an instance method to multiprocessing.Process, but not a multiprocessing.Pool?

从长远来看,如果你坚持使用显然有用的东西,你将遭受最少的谜团:传递模块全局可调用对象(例如,实例方法和本地函数),并显式传递{{ 1}}数据对象(是selftt.lock的实例等等。)

答案 1 :(得分:2)

在Unix操作系统上,通过fork原语创建新进程。

fork原语通过克隆将其分配给子进程的父进程内存地址空间来工作。孩子将拥有父母的内存副本以及文件描述符和共享对象的副本。

这意味着,当你调用fork时,如果父文件已打开,那么孩子也会拥有它。这同样适用于共享对象,如管道,套接字等...

在Unix + CPython中,Locks通过sem_open原语实现,该原语在分叉进程时被设计为shared

我通常建议不要混合并发(特别是多处理)和OOP,因为它经常导致这种误解。

编辑:

刚才看到你正在使用Windows。 Tim Peters给出了正确的答案。为了抽象,Python试图通过其API提供与OS无关的行为。当调用实例方法时,它将pickle对象并通过管道发送它。从而提供与Unix类似的行为。

我建议您阅读programming guidelines进行多处理。特别是在第一点上解决了您的问题:

  

避免共享状态

     

应尽可能避免在流程之间转移大量数据。

     

最好坚持使用队列或管道进行进程之间的通信,而不是使用较低级别的同步原语。