Python : 在生成的进程之间共享一个锁

Python : sharing a lock between spawned processes

最终目标是在后台执行一个方法,但不是并行执行:当多个对象调用此方法时,每个对象都应等待轮到他们继续执行。要在后台实现 运行ning,我必须 运行 子进程(而不是线程)中的方法,并且我需要使用 spawn(而不是 fork)启动它。为了防止并行执行,显而易见的解决方案是在进程之间共享一个全局锁。
当进程被分叉时,这是 Unix 上的默认设置,很容易实现,如下面两个代码中突出显示的那样。
我们可以将其作为 class 变量共享:

import multiprocessing as mp
from time import sleep

class OneAtATime:

    l = mp.Lock()

    def f(self):
        with self.l:
            sleep(1)
        print("Hello")

if __name__ == "__main__":
    a = OneAtATime()
    b = OneAtATime()
    p1 = mp.Process(target = a.f)
    p2 = mp.Process(target = b.f)
    p1.start()
    p2.start()

或者我们可以将它传递给方法:

import multiprocessing as mp
from time import sleep

class OneAtATime:
    def f(self, l):
        with l:
            sleep(1)
        print("Hello")

if __name__ == "__main__":
    a = OneAtATime()
    b = OneAtATime()
    m = mp.Manager()
    l = mp.Lock()
    p1 = mp.Process(target = a.f, args = (l,))
    p2 = mp.Process(target = b.f, args = (l,))
    p1.start()
    p2.start()

这两个代码都具有以一秒为间隔打印 "hello" 的适当行为。 但是,当将 start method 更改为 'spawn' 时,它们会损坏。
第一个 (1) 同时打印两个 "hello"。这是因为,所以他们没有相同的锁。
第二个 (2) 在 运行 时因 FileNotFoundError 失败。我认为这与锁不能被 pickle 的事实有关:见 Python sharing a lock between processes.
在这个答案中,提出了两个修复建议(旁注:我不能使用池,因为我想随机创建任意数量的进程)。
我还没有找到适应第二个修复的方法,但我尝试实现第一个:

import multiprocessing as mp
from time import sleep

if __name__ == "__main__":
    mp.set_start_method('spawn')

class OneAtATime:
    def f(self, l):
        with l:
            sleep(1)
        print("Hello")

if __name__ == "__main__":
    a = OneAtATime()
    b = OneAtATime()
    m = mp.Manager()
    l = m.Lock()
    p1 = mp.Process(target = a.f, args = (l,))
    p2 = mp.Process(target = b.f, args = (l,))
    p1.start()
    p2.start()

由于 AttributeError 和 FileNotFoundError (3) 而失败。事实上,当使用 fork 方法时,它也会失败 (BrokenPipe) (4).
在生成的进程之间共享锁的正确方法是什么?
对我编号的四个失败进行快速解释也很好。 我在 Archlinux 下 运行ning Python 3.6。

恭喜,您已完成 90% 的进度。最后一步其实也不难

是的,您的最终代码块因 AttributeError 而失败,但具体错误是什么? "Can't get attribute 'OneAtATime' on "。这与您已经遇到的问题非常相似 - 它不是酸洗 class OneAtATime。

我进行了以下更改,效果如您所愿:

file ooat.py:

from time import sleep

class OneAtATime:
    def f(self, l):
        with l:
            sleep(1)
        print("Hello")

interactive shell:

import multiprocessing as mp
from oaat import OneAtATime
if __name__ == "__main__":
    mp.set_start_method('spawn')
    a = OneAtATime()
    b = OneAtATime()
    m = mp.Manager()
    l = m.Lock()
    p1 = mp.Process(target = a.f, args = (l,))
    p2 = mp.Process(target = b.f, args = (l,))
    p1.start()
    p2.start()

您可能会注意到,我实际上并没有做任何事情 - 只是将您的代码分成两个单独的文件。试一试,您会发现效果很好。 (至少,它对我有用,在 ubuntu 上使用 python 3.5。)

最后一个代码片段有效,前提是脚本不会过早退出。加入进程就足够了:

import multiprocessing as mp
from time import sleep

class OneAtATime:
    def f(self, l):
        with l:
            sleep(1)
        print("Hello")

if __name__ == "__main__":
    mp.set_start_method('spawn')
    a = OneAtATime()
    b = OneAtATime()
    m = mp.Manager()
    l = m.Lock()
    p1 = mp.Process(target = a.f, args = (l,))
    p2 = mp.Process(target = b.f, args = (l,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()

有关它导致的错误的更多信息,请点击此处 。