[英]Python multithreading + multiprocessing BrokenPipeError (subprocess not exiting?)
当使用multiprocessing.JoinableQueue生成进程的线程时,我得到BrokenPipeError。 它似乎发生在程序完成工作并尝试退出之后,因为它完成它应该做的任何事情。 这是什么意思,有没有办法解决这个/安全忽略?
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
....
(为其他线程剪切相同的错误。)
如果我将JoinableQueue切换到queue.Queue用于多线程部分,那么一切都会修复,但为什么呢?
发生这种情况的原因是,当主线程退出时,您将在multiprocessing.Queue.get
调用中保留后台线程阻塞,但它仅在某些条件下发生:
multiprocessing.Queue.get
.Queue.get上运行并阻塞。 multiprocessing.Process
。 multiprocessing
上下文不是'fork'
。 异常是告诉您multiprocessing.JoinableQueue
.JoinableQueue在get()
调用内部发送EOF
时正在侦听的Connection
的另一端。 通常这意味着Connection
的另一侧已关闭。 在关机期间发生这种情况是有道理的 - Python在退出解释器之前清理所有对象,而部分清理涉及关闭所有打开的Connection
对象。 我还没弄清楚的是,为什么它只会(并且总是)发生,如果已经生成了multiprocessing.Process
( 没有分叉,这就是默认情况下它不会在Linux上发生的原因)并且仍然在运行。 如果我创建一个只在while
循环中休眠的multiprocessing.Process
,我甚至可以重现它。 它根本不需要任何Queue
对象。 无论出于何种原因,运行的,生成的子进程的存在似乎保证将引发异常。 它可能只是导致事物被破坏的顺序恰好适合竞争条件发生,但这是一种猜测。
在任何情况下,使用queue.Queue
而不是multiprocessing.JoinableQueue
是修复它的好方法,因为你实际上并不需要multiprocessing.Queue
queue.Queue
。 您还可以通过将标记发送到其队列来确保在主线程之前关闭后台线程和/或后台进程。 所以,让两个run
方法检查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()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.