将处理请求response.content放入队列后,multiprocessing.Process不会终止

时间:2018-05-23 08:34:59

标签: python python-3.x python-requests python-multiprocessing

我正在尝试与多处理.Process和请求并行运行多个API请求。我将urls解析为JoinableQueue实例并将内容放回Queue实例。我注意到将response.content放入Queue会以某种方式阻止进程终止。

以下是仅使用1个进程(Python 3.5)的简化示例:

import multiprocessing as mp
import queue
import requests
import time


class ChildProcess(mp.Process):
    def __init__(self, q, qout):
        super().__init__()
        self.qin = qin
        self.qout = qout
        self.daemon = True

    def run(self):
        while True:
            try:
                url = self.qin.get(block=False)
                r = requests.get(url, verify=False)
                self.qout.put(r.content)
                self.qin.task_done()
            except queue.Empty:
                break
            except requests.exceptions.RequestException as e:
                print(self.name, e)
                self.qin.task_done()
        print("Infinite loop terminates")


if __name__ == '__main__':
    qin = mp.JoinableQueue()
    qout = mp.Queue()
    for _ in range(5):
        qin.put('http://en.wikipedia.org')
    w = ChildProcess(qin, qout)
    w.start()
    qin.join()
    time.sleep(1)
    print(w.name, w.is_alive())

运行代码后我得到:

  

无限循环终止

     

ChildProcess-1 True

请帮助理解为什么在运行函数退出后进程不会终止。

更新:添加了print语句以显示循环终止

3 个答案:

答案 0 :(得分:1)

根据Queue documentation很难弄清楚这一点-我在同一个问题上苦苦挣扎。

此处的关键概念是,生产者线程终止之前,它会将其具有put数据的所有队列加入;然后该连接将阻塞,直到队列的后台线程终止为止,只有在队列为空时才会发生。因此,基本上,在您的ChildProcess退出之前,必须有人将put的所有内容消耗到队列中!

有一些Queue.cancel_join_thread函数的文档,应该可以解决此问题,但是我无法使它产生任何效果-也许我没有正确使用它。

这是您可以进行修改的示例,可以解决此问题:

if __name__ == '__main__':
    qin = mp.JoinableQueue()
    qout = mp.Queue()
    for _ in range(5):
        qin.put('http://en.wikipedia.org')
    w = ChildProcess(qin, qout)
    w.start()
    qin.join()
    while True:
        try:
            qout.get(True, 0.1)     # Throw away remaining stuff in qout (or process it or whatever,
                                    # just get it out of the queue so the queue background process
                                    # can terminate, so your ChildProcess can terminate.
        except queue.Empty:
            break
    w.join()                # Wait for your ChildProcess to finish up.
    # time.sleep(1)         # Not necessary since we've joined the ChildProcess
    print(w.name, w.is_alive())

答案 1 :(得分:0)

在打印消息上方添加对w.terminate()的调用。

关于为什么这个过程不会自行终止;你的函数代码是一个无限循环,所以它永远不会返回。呼叫终止表示该过程自杀。

答案 2 :(得分:0)

Pipes and Queues documentation

中所述
  

如果子进程已将项目放入队列(并且尚未使用   JoinableQueue.cancel_join_thread),那么该进程将不会   终止,直到所有缓冲项目都已冲洗到管道中。

     

这意味着,如果您尝试加入该过程,则可能会陷入僵局   除非您确定所有已放入队列中的项目   已被消耗。

     

...

     

请注意,使用管理员创建的队列不存在此问题。

如果切换到管理员队列,则该过程将成功终止:

import multiprocessing as mp
import queue
import requests
import time


class ChildProcess(mp.Process):
    def __init__(self, q, qout):
        super().__init__()
        self.qin = qin
        self.qout = qout
        self.daemon = True

    def run(self):
        while True:
            try:
                url = self.qin.get(block=False)
                r = requests.get(url, verify=False)
                self.qout.put(r.content)
                self.qin.task_done()
            except queue.Empty:
                break
            except requests.exceptions.RequestException as e:
                print(self.name, e)
                self.qin.task_done()
        print("Infinite loop terminates")


if __name__ == '__main__':
    manager = mp.Manager()
    qin = mp.JoinableQueue()
    qout = manager.Queue()
    for _ in range(5):
        qin.put('http://en.wikipedia.org')
    w = ChildProcess(qin, qout)
    w.start()
    qin.join()
    time.sleep(1)
    print(w.name, w.is_alive())