我正在尝试在多个执行I / O绑定任务的threading.Thread
与多个执行CPU绑定任务的multiprocessing.Process
之间进行通信。每当线程为某个进程找到工作时,它将与multiprocessing.Queue
的发送端一起放在multiprocessing.Pipe(duplex=False)
上。然后,这些过程将发挥自己的作用,并通过管道将结果发送回线程。此程序似乎在大约70%的情况下都有效,另外30%的我收到了AttributeError: Can't get attribute 'DupFd' on <module 'multiprocessing.resource_sharer' from '/usr/lib/python3.5/multiprocessing/resource_sharer.py'>
要复制:
import multiprocessing
import threading
import time
def thread_work(work_queue, pipe):
while True:
work_queue.put((threading.current_thread().name, pipe[1]))
received = pipe[0].recv()
print("{}: {}".format(threading.current_thread().name, threading.current_thread().name == received))
time.sleep(0.3)
def process_work(work_queue):
while True:
thread, pipe = work_queue.get()
pipe.send(thread)
work_queue = multiprocessing.Queue()
for i in range(0,3):
receive, send = multiprocessing.Pipe(duplex=False)
t = threading.Thread(target=thread_work, args=[work_queue, (receive, send)])
t.daemon = True
t.start()
for i in range(0,2):
p = multiprocessing.Process(target=process_work, args=[work_queue])
p.daemon = True
p.start()
time.sleep(5)
我看了一下多处理source code,但是不明白为什么会发生此错误。
我尝试使用queue.Queue
或带有duplex=True
的管道(默认),但在错误中找不到模式。有人知道如何调试吗?
答案 0 :(得分:2)
您在这里分叉一个已经是多线程的主进程。一般而言,这是有问题的。
这实际上很容易出现问题(不仅限于Python)。规则是“分叉之后而不是之前的线程”。否则,线程执行器使用的锁将在各个进程之间重复。如果其中一个进程在拥有锁的同时死亡,则使用该锁的所有其他进程将死锁-Raymond Hettinger。
引发错误的原因显然是在子进程中管道的文件描述符复制失败。
要解决此问题,请在主进程仍为单线程的情况下创建子进程,或使用其他start_method
创建新进程,例如“ spawn”(Windows上的默认设置)或“ forkserver” '(如果有)。
forkserver
当程序启动并选择forkserver start方法时,服务器进程将启动。从此以后,每当需要新进程时,父进程就连接到服务器并请求它派生一个新进程。 fork服务器进程是单线程的,因此使用os.fork()是安全的。没有多余的资源被继承。
在Unix平台上可用,该平台支持通过Unix管道传递文件描述符。 docs
您可以通过以下方式指定另一个start_method:
multiprocessing.set_start_method(方法) 设置用于启动子进程的方法。方法可以是“ fork”,“ spawn”或“ forkserver”。
请注意,该调用最多应调用一次,并且应在主模块的if name ==' main '子句中进行保护。 docs
有关特定start_method的基准(在Ubuntu 18.04上),请查看here。