我想使用队列来保存结果,因为我希望消费者(序列不并行)在工人产生结果时处理工人的结果。
目前,我想知道为什么以下程序会挂起。
import multiprocessing as mp
import time
import numpy as np
def worker(arg):
time.sleep(0.2)
q, arr = arg
q.put(arr[0])
p = mp.Pool(4)
x = np.array([4,4])
q = mp.Queue()
for i in range(4):
x[0] = i
#worker((q,x))
p.apply_async(worker, args=((q, x),))
print("done_apply")
time.sleep(0.2)
for i in range(4):
print(q.get())
答案 0 :(得分:1)
将apply_async更改为apply以显示错误消息:
"Queue objects should only be shared between processes through inheritance"
解决方案:
import multiprocessing as mp
import time
import numpy as np
def worker(arg):
time.sleep(0.2)
q, arr = arg
q.put(arr[0])
p = mp.Pool(4)
x = np.array([4,4])
m = mp.Manager()
q = m.Queue()
for i in range(4):
x[0] = i
#worker((q,x))
p.apply_async(worker, args=((q, x),))
print("done_apply")
time.sleep(0.2)
for i in range(4):
print(q.get())
结果:
done_apply
3
3
3
3
显然,我需要手动制作numpy数组的副本,因为所需的结果应该是0,1,2,3,而不是3,3,3,3。
答案 1 :(得分:1)
Queue
个对象无法共享。我首先通过找到answer来得出与OP相同的结论。
不幸的是,此代码中还存在其他问题(并未使其与链接答案完全相同)
worker(arg)
应为worker(*arg)
,以便解压缩工作。没有它,我的过程也被锁定了(我承认我不知道为什么。它应该抛出异常,但我想多处理和例外不能很好地协同工作)x
传递给工作人员会产生与结果相同的数字(apply
可以使用,但不能使用apply_async
另一件事:为了使代码可移植,请将主代码包装在if __name__ == "__main__":
,这是因为流程产生的差异而在Windows上需要
为我输出0,3,2,1的完全固定的代码:
import multiprocessing as mp
import time
import numpy as np
def worker(*arg): # there are 2 arguments to "worker"
#def worker(q, arr): # is probably even better
time.sleep(0.2)
q, arr = arg
q.put(arr[0])
if __name__ == "__main__":
p = mp.Pool(4)
m = mp.Manager() # use a manager, Queue objects cannot be shared
q = m.Queue()
for i in range(4):
x = np.array([4,4]) # create array each time (or make a copy)
x[0] = i
p.apply_async(worker, args=(q, x))
print("done_apply")
time.sleep(0.2)
for i in range(4):
print(q.get())
答案 2 :(得分:1)
我认为您选择将multiprocessing.Pool
与queue
一起使用是您遇到的主要问题的根源。使用池会预先创建子进程,稍后将分配这些作业。但是,由于您无法(轻松)将queue
传递给现有流程,因此不能很好地匹配您的问题。
相反,您应该删除自己的队列并使用池中内置的队列来获取return
编辑的值worker
或完全废弃池并使用multiprocessing.Process
为您必须完成的每项任务启动新流程。
我还注意到你的代码在修改x
数组的主线程与在旧值发送到之前序列化旧值的线程之间的主进程中存在竞争条件。一个工人的过程。很多时候你可能最终会发送同一个数组的副本(带有最终值),而不是你想要的几个不同的值。
这是一个快速且未经测试的版本,可以删除队列:
def worker(arr):
time.sleep(0.2)
return arr[0]
if __name__ == "__main__":
p = mp.Pool(4)
results = p.map(worker, [np.array([i, 4]) for i in range(4)])
p.join()
for result in results:
print(result)
这是一个删除Pool
并保留队列的版本:
def worker(q, arr):
time.sleep(0.2)
q.put(arr[0])
if __name__ == "__main__":
q = m.Queue()
processes = []
for i in range(4):
p = mp.Process(target=worker, args=(q, np.array([i, 4])))
p.start()
processes.append(p)
for i in range(4):
print(q.get())
for p in processes:
p.join()
请注意,在上一个版本中,在我们尝试get
进程之前,我们join
队列中的结果可能很重要(尽管如果我们只处理四个进程,可能并非如此)值)。如果队列要填满,如果我们以其他顺序执行,则可能发生死锁。可能会阻止工作程序尝试写入队列,而主进程被阻塞,等待工作进程退出。