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

5

我正在尝试在进程之间共享锁。我知道共享锁的方法是将其作为参数传递给目标函数。但是,即使像下面这样的方法也可以工作。我不明白进程是如何共享这个锁的。请问有谁能解释一下吗?

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 的内容。 - President James K. Polk
@JamesKPolk 我正在使用Windows,Python 3.5。 - gdRow
你为什么认为他们在共享锁?如果你删除了“with self.lock”这一行,输出结果是否相同? - Hugh Fisher
如果mp.lock()被实现为@staticmethod,那该怎么办?你没有为multiprocessing类创建任何对象。 - gout
如果我删除self.lock这一行,每个进程都会立即启动并在完成前等待五秒钟。但是当有锁存在时情况并非如此。 - gdRow
2个回答

5
在Windows上(你说你正在使用),这些问题总是涉及到如何使与配合,因为在Windows上,所有Python数据跨进程边界的实现都是通过在发送端进行pickling(并在接收端进行unpickling)来完成的。
我最好的建议是尽量避免这种问题的发生;-)例如,你展示的代码在Python 2下会崩溃,在Python 3下如果使用方法而不是也会崩溃。
这不仅仅是锁定问题,简单地尝试pickle一个绑定方法(比如)在Python 2中就会崩溃。想一想。你要跨越一个进程边界,而在接收端没有一个与对应的对象。在接收端应该绑定到哪个对象?
在Python 3中,对self.run_job进行pickle时,也会pickle self对象的一个副本。所以答案是:在接收端通过魔法创建与self对应的SampleClass对象。整个状态都被pickle,包括t.lock,这就是为什么它可以“工作”的原因。
详细实现细节请参见: 为什么我可以将实例方法传递给multiprocessing.Process,但无法传递给multiprocessing.Pool? 从长远来看,如果您坚持使用明显旨在工作的东西,则最少遇到问题,即传递模块全局可调用对象(例如,不是实例方法或局部函数),并明确传递数据对象(无论是锁、队列、manager.list等等)。

4
在Unix操作系统中,通过fork原语创建新进程。 fork原语的工作方式是克隆父进程的内存地址空间并将其分配给子进程。子进程将拥有父进程的内存副本以及文件描述符和共享对象。
这意味着,当调用fork时,如果父进程打开了文件,则子进程也会拥有该文件。与管道、套接字等共享对象一样。
在Unix+CPython中,是通过sem_open原语实现的,该原语旨在在fork进程时共享
我通常建议不要混合并发(特别是多处理)和面向对象编程,因为这经常导致这种误解。
编辑:
我刚才看到你在使用Windows。Tim Peters 给出了正确的答案。为了实现抽象化,Python 尝试通过其 API 提供独立于操作系统的行为。在调用实例方法时,它会对对象进行 pickle 并将其发送到管道中。从而提供类似 Unix 的行为。
我建议你阅读多进程编程指南。你的问题特别在第一点中得到解决:

避免共享状态

尽可能避免在进程之间传递大量数据。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接