【问题标题】:Python Queue suddenly emptyPython Queue 突然空了
【发布时间】:2015-05-03 05:16:09
【问题描述】:

我正在尝试基于this example 同时运行几个计算。我扩展它的方式不仅仅是一个简单的功能,而是通过子进程运行一些外部软件。它在 Ubuntu 14.04.2 LTS(GNU/Linux 3.16.0-30-generic x86_64)上使用 Python 2.7.6 运行。

涉及到一个队列来跟踪结果输出。这个队列应该被填满,但在计算完成后似乎是空的。

简化后的代码如下所示:

import subprocess, shlex, os, random, pickle
from Queue import Queue
from multiprocessing import Process
from time import sleep

def mp_solve(problems, nprocs):
    def worker(problem, out_q):
        outdict = []
        cmd = "..." + problem
        args = shlex.split(cmd)
        output,error = subprocess.Popen(args,stdout = subprocess.PIPE, stderr= subprocess.PIPE).communicate()
        outdict = [str(problem), str(output)]
        out_q.put(outdict)
        print out_q.empty() #prints False

    out_q = Queue() #create Queue
    procs = []

    for i in range(nprocs):
        p = Process(
                target=worker,
                args=(problems[i][1], out_q))
        procs.append(p)
        p.start()

    sleep(10) #calculations are limited to 3 seconds through a parameter passed to external program

    print out_q.empty() #prints True

    resultlist = []
    for i in range(nprocs):
        print "going to Q" + str(i)
        try:
            resultlist.append(out_q.get())
        except Queue.Empty:
            print "Queue empty"


mp_solve(list_of_problems, 10)

这个的输出将是

False
False
False
False
False
False
False
False
False
False
True
going to Q0

在最后一个命令之后,会话窗口变得无用。我可以输入它,但什么都不会发生,甚至 Ctrl + C 也没有效果。然后我关闭 ssh 会话。

我对多处理还很陌生,我无法弄清楚为什么 Queue 似乎被正确填充(从返回的 False 中可以看出)但后来却是空的。请注意, Queue.Empty 似乎永远不会捕捉到。有什么想法可以让我回到正确的轨道上吗?

【问题讨论】:

  • 看起来你有一个叉子炸弹。您在multiprocessing 的所有示例中看到if __name__ == '__main__' 了吗?
  • 你在哪里调用 mp_solve?
  • @ReutSharabani:这不是专门针对使用Pool吗?
  • @cdarke ,据我所知。这个想法是做某种装箱来将对象全球化到其他进程。据我了解 - 任何 其他进程。但我可能是错的。

标签: python python-2.7 subprocess python-multiprocessing


【解决方案1】:

Queue 模块中的Queue 对象不适合多处理:它仅用于同一进程中的线程之间的通信。请改用 multiprocessing 模块中的 Queue 对象。

from multiprocessing import Process, Queue

这应该可以解决眼前的问题。以下是其他一些注意事项:

  • out_q.get() 是一个阻塞调用:如果队列中没有任何内容,那么它将一直等到有。因此,在您上面的代码中,永远不会引发 Empty 异常。对于非阻塞版本,请尝试out_q.get(block=False)。或者,您可以指定超时:out_q.get(timeout=10.0)

  • 最好在主进程退出之前加入子进程。在每个进程上调用proc.join()。正如 dano 在 cmets 中指出的那样,如果您尝试在清空队列之前加入进程,则可能会出现死锁(请参阅警告here),因此您可能希望在非常mp_solve 函数结束。

  • if __name__ == '__main__': 块保护你的主代码也是一个好习惯。它不会在 Linux 上(在 Python 2.7 下)产生影响,但没有它,您的代码将无法在 Windows 上正确运行。

  • 再次为了跨平台兼容性,您应该将嵌套的worker 函数移到模块级别。要在 Windows 上运行代码,worker 函数必须是可腌制的,而嵌套函数是不可腌制的。

【讨论】:

  • 关于第二点的注意事项 - 如果您的子进程将大量数据放入输出队列,则在将数据拉出 Queue 之前尝试 join 子进程可能会导致僵局。请参阅警告here
  • @dano:我忘记了那个细节。谢谢。
猜你喜欢
  • 2016-10-02
  • 2016-05-19
  • 2021-04-13
  • 1970-01-01
  • 2017-03-27
  • 2012-02-26
  • 2021-06-20
  • 1970-01-01
  • 2017-12-28
相关资源
最近更新 更多