增加迭代时,多进程变为僵尸进程。 mp.Queue()对Manager.list()有什么好处?

时间:2016-08-15 19:19:24

标签: python python-2.7 multiprocessing

我试图使用多处理来产生4个进程,这些进程强制进行一些计算,并且每个迭代在我想要在它们之间共享时,每个迭代操作单个列表对象的可能性非常小。指南中没有best practice,但我需要很多手"。

代码适用于相对较少的迭代次数,但当将数量增加到某个阈值时,所有四个进程都将进入僵尸状态。他们默默地失败了。

我尝试使用multiprocessing.Queue()跟踪对共享列表的修改。它出现在this SO postthis closed Python issue – "not a bug"以及几个引用这些内容的帖子中,底层管道可能会过载而进程只会挂起。由于过多的代码,SO帖子中接受的答案极难破译。

为清晰起见编辑:
文档中的示例执行非常轻量级的操作,几乎总是单个函数调用。因此,我不知道我是否误解和滥用功能。

准则说:

  

最好坚持使用队列或管道   进程之间的通信而不是使用较低级别   来自线程模块的同步原语。

"沟通"这里的意思不是我在我的例子中实际做的事情吗? 或
这是否意味着我应该在队列中而不是与经理共享my_list?在每个流程的每次迭代中,这不是queue.getqueue.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) 

1 个答案:

答案 0 :(得分:1)

队列与列表

在引擎盖下,multiprocessing.Queuemanager.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