python:为什么加入让我久等了?

时间:2015-09-02 07:30:57

标签: python join queue multiprocessing

我想在10,000个型号上进行聚类。在此之前,我必须计算与每两个模型相关的皮尔逊相关系数。这是一个大量的计算,所以我使用多处理来产生进程,将计算工作分配给16 cpus。我的代码是这样的:

import numpy as np
from multiprocessing import Process, Queue

def cc_calculator(begin, end, q):
    index=lambda i,j,n: i*n+j-i*(i+1)/2-i-1
    for i in range(begin, end):
        for j in range(i, nmodel):
            all_cc[i][j]=get_cc(i,j)
            q.put((index(i,j,nmodel),all_cc[i][j]))

def func(i):
    res=(16-i)/16
    res=res**0.5
    res=int(nmodel*(1-res))
    return res

nmodel=int(raw_input("Entering the number of models:"))
all_cc=np.zeros((nmodel,nmodel))
ncc=int(nmodel*(nmodel-1)/2)
condensed_cc=[0]*ncc
q=Queue()
mprocess=[]

for ii in range(16):
    begin=func(i)
    end=func(i+1)
    p=Process(target=cc_calculator,args=(begin,end,q))
    mprocess+=[p]
    p.start()

for x in mprocess:
    x.join()

while not q.empty():
    (ind, value)=q.get()
    ind=int(ind)
    condensed_cc[ind]=value
np.save("condensed_cc",condensed_cc)

其中get_cc(i,j)计算与模型i和j相关的相关系数。 all_cc是上三角矩阵,all_cc [i] [j]存储cc值。 condensed_cc是all_cc的另一个版本。我将处理它以实现聚类的缩放。 “func”函数有助于为每个cpu分配几乎相同的计算量。

我使用nmodel = 20成功运行程序。当我尝试使用nmodel = 10,000运行程序时,似乎它永远不会结束。我等待大约两天并在另一个终端窗口中使用top命令,没有使用命令“python”的进程仍在运行。但程序仍在运行,没有输出文件。我使用Ctrl + C强制它停止,它指向行:x.join()。 nmodel = 40跑得快,但失败了同样的问题。

也许这个问题与q有关。因为如果我评论行:q.put(...),它会成功运行。或类似的东西:

q.put(...)
q.get()

也没关系。但是这两种方法都不会给出正确的浓缩_cc。它们不会更改all_cc或condensed_cc。

另一个只有一个子流程的例子:

from multiprocessing import Process, Queue

def g(q):
    num=10**2
    for i in range(num):
        print '='*10
        print i
        q.put((i,i+2))
        print "qsize: ", q.qsize()

q=Queue()
p=Process(target=g,args=(q,))
p.start()
p.join()

while not q.empty():
    q.get()

没有num = 100但是num = 10,000就失败了。即使使用num = 100 ** 2,他们也会打印所有i和q.qsizes。我无法弄清楚为什么。此外,Ctrl + C会导致追溯到p.join()。

我想更多地谈谈队列的大小问题。 Documentation about Queue and its put method引入Queue作为队列([maxsize]),并且它说明了put方法:...如果需要阻塞,直到有空闲插槽可用。这些都使得人们认为由于队列空间不足而阻塞了子进程。但是,正如我之前在第二个例子中提到的那样,屏幕上打印的结果证明了qsize的增加,这意味着队列未满。我添加一行:

print q.full()

在print size语句之后,对于num = 10,000,它始终为false,而程序仍然卡在某处。强调一件事:另一个终端中的top命令显示没有使用命令python的进程。这真让我困惑。

我正在使用python 2.7.9。

1 个答案:

答案 0 :(得分:0)

我相信您遇到的问题在多处理编程指南中有所描述:docs

特别是本节:

  

加入使用队列的进程

     

请记住,将项目放入队列的进程将在终止之前等待,直到所有缓冲的项目由“feeder”线程提供给底层管道。 (子进程可以调用队列的cancel_join_thread()方法来避免这种行为。)

     

这意味着无论何时使用队列,您都需要确保在加入进程之前最终删除已放入队列的所有项目。否则,您无法确定已将项目放入队列的进程将终止。还要记住,非守护进程将自动加入。

     

将导致死锁的示例如下:

from multiprocessing import Process, Queue

def f(q):
    q.put('X' * 1000000)

if __name__ == '__main__':
    queue = Queue()
    p = Process(target=f, args=(queue,))
    p.start()
    p.join()                    # this deadlocks
    obj = queue.get()
     

这里的修复方法是交换最后两行(或者只是删除p.join()行。)

您可能还想查看关于"避免共享状态"。

的部分

看起来您正在使用.join来避免q.empty()在添加内容之前返回True的竞争条件。在使用多处理(或多线程)时,您根本不应该依赖.empty()。相反,当完成向队列中添加项目时,您应该通过信令从工作进程处理到主进程。这通常是通过在队列中放置 sentinal值来完成的,但也有其他选项。