【问题标题】:Python: Why is the multiprocessing lock shared among processes here?Python:为什么这里的进程之间共享多进程锁?
【发布时间】:2017-12-14 20:50:10
【问题描述】:

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

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

【问题讨论】:

  • 也许你在一个 Unix 系统上。阅读fork
  • @JamesKPolk 我使用的是 Windows,Python 3.5。
  • 是什么让您认为他们共享锁?如果删除 with self.lock 行,是否得到相同的输出?
  • 如果 mp.lock() 被实现为 @staticmethod 怎么办?您没有为多处理类创建任何对象。
  • @HughFisher 如果我删除 self.lock 行,每个进程都会立即启动并在完成前等待五秒钟。有锁时不是这种情况

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


【解决方案1】:

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

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

这意味着,当您调用 fork 时,如果父级打开了一个文件,则子级也将拥有它。这同样适用于管道、套接字等共享对象......

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

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

编辑:

刚才看到您正在使用 Windows。 Tim Peters 给出了正确答案。为了抽象起见,Python 试图通过其 API 提供与操作系统无关的行为。调用实例方法时,它将腌制对象并通过管道发送。因此提供了与 Unix 类似的行为。

我建议您阅读programming guidelines 以了解多处理。您的问题特别在第一点得到解决:

避免共享状态

应尽量避免在进程之间转移大量数据。

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

【讨论】:

    【解决方案2】:

    在 Windows(你说你正在使用)上,这些事情总是减少到关于 multiprocessing 如何与 pickle 一起玩的细节,因为 Windows 上所有跨越进程边界的 Python 数据都是通过在发送时酸洗来实现的结束(并在接收端取消腌制)。

    我最好的建议是避免一开始就提出这样的问题 ;-) 例如,您展示的代码在 Python 2 下的 Windows 上会爆炸,如果您使用 multiprocessing.Pool,也会在 Python 3 下爆炸方法而不是multiprocessing.Process

    这不仅仅是锁,简单地尝试腌制一个绑定方法(如self.run_job)在 Python 2 中爆炸了。想想看。您正在跨越进程边界,并且没有在接收端对应于self 的对象。 self.run_job 应该在接收端绑定到什么对象?

    在 Python 3 中,酸洗 self.run_job also 酸洗 self 对象的副本。这就是答案:对应于selfSampleClass 对象是在接收端通过魔术创建的。清如泥。 t 的整个状态都是腌制的,包括 t.lock。这就是它“起作用”的原因。

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

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

    从长远来看,如果你坚持显然是为了工作的事情,你将遭受最少的谜团:传递模块全局可调用对象(既不是实例方法也不是局部函数),并显式传递 @987654335 @ 数据对象(LockQueuemanager.list 等的实例)。

    【讨论】:

      猜你喜欢
      • 2014-10-22
      • 1970-01-01
      • 1970-01-01
      • 2017-05-04
      • 2021-10-10
      • 2020-09-13
      • 2012-07-03
      • 2012-07-22
      • 1970-01-01
      相关资源
      最近更新 更多