Python:在衍生进程之间共享锁定

时间:2017-06-22 14:23:37

标签: python multiprocessing python-multiprocessing

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

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

这两个代码都有适当的打印行为"你好"间隔一秒钟。 但是,当将start method更改为' spawn'时,它们会被破坏 第一个(1)同时打印"你好" s。这是因为the internal state of a class is not pickled,所以他们没有相同的锁 第二个(2)在运行时因FileNotFoundError而失败。我认为这与锁不能被腌制的事实有关:见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下运行Python 3.6。

2 个答案:

答案 0 :(得分:2)

恭喜你,你有90%的路要走。最后一步实际上并不难做到。

是的,您的最终代码块因AttributeError而失败,但具体是什么错误? “无法获取'OneAtATime'属性”。这与你已经遇到过的问题非常相似 - 它不是在挑选OneAtATime类。

我做了以下更改,并且按照您的意愿运行:

  

file ooat.py:

from time import sleep

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

交互式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。)

答案 1 :(得分:2)

如果脚本未提前退出,则最后一个代码段可以正常工作。加入流程就足够了:

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

有关此处导致的错误的更多信息https://stackoverflow.com/a/25456494/8194503