Python多线程+;多处理断开管道错误(子进程未退出?)
当采用multiprocessing.JoinableQueue的线程生成进程时,我遇到BrokenPipeError。这似乎是在程序完成工作并试图退出后发生的,因为它完成了所有它应该做的事情。这意味着什么,有没有办法解决这个问题/安全地忽略它Python多线程+;多处理断开管道错误(子进程未退出?),python,multithreading,multiprocessing,Python,Multithreading,Multiprocessing,当采用multiprocessing.JoinableQueue的线程生成进程时,我遇到BrokenPipeError。这似乎是在程序完成工作并试图退出后发生的,因为它完成了所有它应该做的事情。这意味着什么,有没有办法解决这个问题/安全地忽略它 import requests import multiprocessing from multiprocessing import JoinableQueue from queue import Queue import threading cla
import requests
import multiprocessing
from multiprocessing import JoinableQueue
from queue import Queue
import threading
class ProcessClass(multiprocessing.Process):
def __init__(self, func, in_queue, out_queue):
super().__init__()
self.in_queue = in_queue
self.out_queue = out_queue
self.func = func
def run(self):
while True:
arg = self.in_queue.get()
self.func(arg, self.out_queue)
self.in_queue.task_done()
class ThreadClass(threading.Thread):
def __init__(self, func, in_queue, out_queue):
super().__init__()
self.in_queue = in_queue
self.out_queue = out_queue
self.func = func
def run(self):
while True:
arg = self.in_queue.get()
self.func(arg, self.out_queue)
self.in_queue.task_done()
def get_urls(host, out_queue):
r = requests.get(host)
out_queue.put(r.text)
print(r.status_code, host)
def get_title(text, out_queue):
print(text.strip('\r\n ')[:5])
if __name__ == '__main__':
def test():
q1 = JoinableQueue()
q2 = JoinableQueue()
for i in range(2):
t = ThreadClass(get_urls, q1, q2)
t.daemon = True
t.setDaemon(True)
t.start()
for i in range(2):
t = ProcessClass(get_title, q2, None)
t.daemon = True
t.start()
for host in ("http://ibm.com", "http://yahoo.com", "http://google.com", "http://amazon.com", "http://apple.com",):
q1.put(host)
q1.join()
q2.join()
test()
print('Finished')
程序输出:
200 http://ibm.com
<!DOC
200 http://google.com
<!doc
200 http://yahoo.com
<!DOC
200 http://apple.com
<!DOC
200 http://amazon.com
<!DOC
Finished
Exception in thread Thread-2:
Traceback (most recent call last):
File "C:\Python\33\lib\multiprocessing\connection.py", line 313, in _recv_bytes
nread, err = ov.GetOverlappedResult(True)
BrokenPipeError: [WinError 109]
The pipe has been ended
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Python\33\lib\threading.py", line 901, in _bootstrap_inner
self.run()
File "D:\Progs\Uspat\uspat\spider\run\threads_test.py", line 31, in run
arg = self.in_queue.get()
File "C:\Python\33\lib\multiprocessing\queues.py", line 94, in get
res = self._recv()
File "C:\Python\33\lib\multiprocessing\connection.py", line 251, in recv
buf = self._recv_bytes()
File "C:\Python\33\lib\multiprocessing\connection.py", line 322, in _recv_bytes
raise EOFError
EOFError
....
200http://ibm.com
(对其他螺纹剪切相同的错误。)
如果我将JoinableQueue切换到多线程部分的queue.queue,一切都会修复,但原因是什么?发生这种情况是因为您在主线程退出时将后台线程阻塞在多处理.queue.get
调用中,但它只在某些情况下发生:
当主线程退出时,守护进程线程正在运行并阻塞多处理.Queue.get
一个多处理。进程正在运行
多处理
上下文不是“fork”
例外情况是,多处理.JoinableQueue
正在侦听的连接的另一端在get()
调用的内部发送了EOF
时。通常这意味着连接的另一侧已关闭。这在关机期间发生是有意义的——Python在退出解释器之前清理所有对象,清理的一部分包括关闭所有打开的连接
对象。我还没有弄清楚的是,为什么只有当一个多处理.Process
已经生成(而不是分叉,这就是为什么默认情况下它不会在Linux上发生)并且仍在运行时才会发生(而且总是发生)。如果我创建了一个只在while
循环中休眠的多处理.Process
,我甚至可以复制它。它根本不接受任何队列
对象。无论出于何种原因,出现一个正在运行的派生子进程似乎可以保证引发异常。它可能只是导致事物被破坏的顺序恰好适合于种族条件的发生,但这只是一个猜测
在任何情况下,使用queue.queue
而不是multiprocessing.JoinableQueue
都是一种很好的修复方法,因为实际上不需要multiprocessing.queue
。您还可以通过向后台线程和/或后台进程的队列发送sentinel来确保后台线程和/或后台进程在主线程之前关闭。因此,让两个运行方法检查哨兵:
def run(self):
for arg in iter(self.in_queue.get, None): # None is the sentinel
self.func(arg, self.out_queue)
self.in_queue.task_done()
self.in_queue.task_done()
完成后再派哨兵:
threads = []
for i in range(2):
t = ThreadClass(get_urls, q1, q2)
t.daemon = True
t.setDaemon(True)
t.start()
threads.append(t)
p = multiprocessing.Process(target=blah)
p.daemon = True
p.start()
procs = []
for i in range(2):
t = ProcessClass(get_title, q2, None)
t.daemon = True
t.start()
procs.append(t)
for host in ("http://ibm.com", "http://yahoo.com", "http://google.com", "http://amazon.com", "http://apple.com",):
q1.put(host)
q1.join()
# All items have been consumed from input queue, lets start shutting down.
for t in procs:
q2.put(None)
t.join()
for t in threads:
q1.put(None)
t.join()
q2.join()
发生这种情况的原因是,当主线程退出时,后台线程将阻塞在多处理.Queue.get
调用中,但这种情况仅在某些情况下发生:
当主线程退出时,守护进程线程正在运行并阻塞多处理.Queue.get
一个多处理。进程正在运行
多处理
上下文不是“fork”
例外情况是,多处理.JoinableQueue
正在侦听的连接的另一端在get()
调用的内部发送了EOF
时。通常这意味着连接的另一侧已关闭。这在关机期间发生是有意义的——Python在退出解释器之前清理所有对象,清理的一部分包括关闭所有打开的连接
对象。我还没有弄清楚的是,为什么只有当一个多处理.Process
已经生成(而不是分叉,这就是为什么默认情况下它不会在Linux上发生)并且仍在运行时才会发生(而且总是发生)。如果我创建了一个只在while
循环中休眠的多处理.Process
,我甚至可以复制它。它根本不接受任何队列
对象。无论出于何种原因,出现一个正在运行的派生子进程似乎可以保证引发异常。它可能只是导致事物被破坏的顺序恰好适合于种族条件的发生,但这只是一个猜测
在任何情况下,使用queue.queue
而不是multiprocessing.JoinableQueue
都是一种很好的修复方法,因为实际上不需要multiprocessing.queue
。您还可以通过向后台线程和/或后台进程的队列发送sentinel来确保后台线程和/或后台进程在主线程之前关闭。因此,让两个运行方法检查哨兵:
def run(self):
for arg in iter(self.in_queue.get, None): # None is the sentinel
self.func(arg, self.out_queue)
self.in_queue.task_done()
self.in_queue.task_done()
完成后再派哨兵:
threads = []
for i in range(2):
t = ThreadClass(get_urls, q1, q2)
t.daemon = True
t.setDaemon(True)
t.start()
threads.append(t)
p = multiprocessing.Process(target=blah)
p.daemon = True
p.start()
procs = []
for i in range(2):
t = ProcessClass(get_title, q2, None)
t.daemon = True
t.start()
procs.append(t)
for host in ("http://ibm.com", "http://yahoo.com", "http://google.com", "http://amazon.com", "http://apple.com",):
q1.put(host)
q1.join()
# All items have been consumed from input queue, lets start shutting down.
for t in procs:
q2.put(None)
t.join()
for t in threads:
q1.put(None)
t.join()
q2.join()
感谢您的全面回答(附带说明,可能对某人有帮助:我想使用multiprocessing.JoinableQueue而不是queue.queue,以便能够将参数从多处理部分传递回应用程序的多线程部分,尽管我上面的示例中没有这样的代码),感谢您的全面回答(旁注,可能对某些人有帮助:我想使用multiprocessing.JoinableQueue而不是queue.queue,以便能够将参数从多处理部分传递回应用程序的多线程部分,尽管在我上面的示例中没有这样的代码)