最终目标是在后台执行一个方法,而不是并行执行:当多个对象调用此方法时,每个对象都应该等待轮到它们继续。为了实现在后台运行,我必须在子进程(而不是线程)中运行该方法,并且我需要使用 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()
这两个代码都具有以一秒间隔打印"hello"的适当行为。 但是,当将启动方法更改为"生成"时,它们会损坏。
第一 (1) 个同时打印两个"hello"。这是因为类的内部状态不是酸洗的,所以它们没有相同的锁。
第二 (2) 在运行时失败,并出现 FileNotFoundError。我认为这与锁不能被腌制的事实有关:参见 Python 在进程之间共享锁。
在此答案中,建议进行两个修复(旁注:我无法使用池,因为我想随机创建任意数量的进程)。
我还没有找到适应第二个修复的方法,但我尝试实现第一个:
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方法时,它也失败了(Broken Pipe)(4)。
在生成的进程之间共享锁的正确方法是什么?
快速解释一下我编号的四个失败也会很好。 我在 Archlinux 下运行 Python 3.6。
恭喜你,你完成了 90% 的路程。 最后一步其实并不难做到。
是的,您的最终代码块因 AttributeError 而失败,但具体错误是什么? "无法在 上获取属性'OneAtATime'"。 这与你已经遇到的问题非常相似 - 它不是腌制类OneAtATime。
我进行了以下更改,它按您的需要工作:
文件 ooat.py:
from time import sleep
class OneAtATime:
def f(self, l):
with l:
sleep(1)
print("Hello")
交互式外壳:
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()
有关它引起的错误的更多信息 此处 https://stackoverflow.com/a/25456494/8194503.