我想在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。
答案 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值来完成的,但也有其他选项。