我试图使用多处理来产生4个进程,这些进程强制进行一些计算,并且每个迭代在我想要在它们之间共享时,每个迭代操作单个列表对象的可能性非常小。指南中没有best practice,但我需要很多手"。
代码适用于相对较少的迭代次数,但当将数量增加到某个阈值时,所有四个进程都将进入僵尸状态。他们默默地失败了。
我尝试使用multiprocessing.Queue()
跟踪对共享列表的修改。它出现在this SO post,this closed Python issue – "not a bug"以及几个引用这些内容的帖子中,底层管道可能会过载而进程只会挂起。由于过多的代码,SO帖子中接受的答案极难破译。
为清晰起见编辑:
文档中的示例执行非常轻量级的操作,几乎总是单个函数调用。因此,我不知道我是否误解和滥用功能。
准则说:
最好坚持使用队列或管道 进程之间的通信而不是使用较低级别 来自线程模块的同步原语。
"沟通"这里的意思不是我在我的例子中实际做的事情吗?
或
这是否意味着我应该在队列中而不是与经理共享my_list
?在每个流程的每次迭代中,这不是queue.get
和queue.put
吗?
如果maxsize小于或等于零,则队列大小为无限。
执行此操作并不能解决我的失败示例中的错误。直到我queue.put()
为止,所有数据都存储在普通的Python列表中:my_return_list
所以由于我提供的链接,这实际上是失败了吗?
与目前的解决方法相比,有更好的方法吗?我似乎无法找到其他人采取看起来相似的方法,我觉得我错过了一些东西。我需要这个适用于Windows和Linux。
失败的示例(取决于__main__
下的迭代次数):
import multiprocessing as mp
import random
import sys
def mutate_list(my_list, proc_num, iterations, queue, lock):
my_return_list = []
if iterations < 1001:
# Works fine
for x in xrange(iterations):
if random.random() < 0.01:
lock.acquire()
print "Process {} changed list from:".format(proc_num)
print my_list
print "to"
random.shuffle(my_list)
print my_list
print "........"
sys.stdout.flush()
lock.release()
my_return_list.append([x, list(my_list)])
else:
for x in xrange(iterations):
# Enters zombie state
if random.random() < 0.01:
lock.acquire()
random.shuffle(my_list)
my_return_list.append([x, list(my_list)])
lock.release()
if x % 1000 == 0:
print "Completed iterations:", x
sys.stdout.flush()
queue.put(my_return_list)
def multi_proc_handler(iterations):
manager = mp.Manager()
ns = manager.list()
ns.extend([x for x in range(10)])
queue = mp.Queue()
lock = manager.Lock()
print "Starting list to share", ns
print ns
sys.stdout.flush()
p = [mp.Process(target=mutate_list, args=(ns,x,iterations,queue,lock)) for x in range(4)]
for process in p: process.start()
for process in p: process.join()
output = [queue.get() for process in p]
return output
if __name__ == '__main__':
# 1000 iterations is fine, 100000 iterations will create zombies
multi_caller = multi_proc_handler(100000)
使用multiprocessing.Manager.list()
解决方法:
import multiprocessing as mp
import random
import sys
def mutate_list(my_list, proc_num, iterations, my_final_list, lock):
for x in xrange(iterations):
if random.random() < 0.01:
lock.acquire()
random.shuffle(my_list)
my_final_list.append([x, list(my_list)])
lock.release()
if x % 10000 == 0:
print "Completed iterations:", x
sys.stdout.flush()
def multi_proc_handler(iterations):
manager = mp.Manager()
ns = manager.list([x for x in range(10)])
lock = manager.Lock()
my_final_list = manager.list() # My Queue substitute
print "Starting list to share", ns
print ns
sys.stdout.flush()
p = [mp.Process(target=mutate_list, args=(ns,x,iterations,my_final_list,
lock)) for x in range(4)]
for process in p: process.start()
for process in p: process.join()
return list(my_final_list)
if __name__ == '__main__':
multi_caller = multi_proc_handler(100000)
答案 0 :(得分:1)
队列与列表
在引擎盖下,multiprocessing.Queue
和manager.list()
都在写入和读取缓冲区。
<强>队列强>
shared_queue = multiprocessing.Queue()
当你用N个或更多字节调用put
时(其中N依赖于很多变量),它不仅仅是缓冲区可以处理的put
块。您可以通过在另一个进程中调用put
来解除阻止get
。这是一个使用代码的第一个版本应该很容易执行的实验。我强烈建议您尝试这个实验。
<强>列表强>
manager = multiprocessing.Manager()
shared_list = manager.list()
当你调用append
时,你传递的字节少于N个字节,并且对缓冲区的写入成功。 还有另一个进程从缓冲区中读取数据并将其附加到实际的list
。此过程由manager
创建。即使你用N个或更多字节调用append
,一切都应该继续工作,因为还有另一个从缓冲区读取的进程。您可以通过这种方式将任意数量的字节传递给另一个进程。
<强>摘要强>
希望这可以澄清为什么你的“解决方法”有效。您正在将对缓冲区的写入分解为更小的部分,并且您有一个帮助程序正在从缓冲区读取以将这些部分放入托管list
。