使用线程和进程共享管理器列表会创建单独的副本

时间:2017-12-06 10:30:38

标签: python python-2.7 python-multiprocessing python-multithreading

我想在进程和线程之间共享一个列表。所以我找到了multiprocessing.Manager()来创建列表。我写了下面这段代码:

import multiprocessing
import threading
import time

def consumer(l):
    counter = 0
    while counter < 20:
        time.sleep(5)
        l = l[len(l)/2:]
        print "consumer l: {}".format(l)
        counter += 5

def producer(l):
    for i in range(10):
        time.sleep(i)
        l.append(i)
        print "producer l: {}".format(l)

if __name__=="__main__":
    mgr = multiprocessing.Manager()
    l = mgr.list()

    p = threading.Thread(target=producer, args=(l,))
    c = multiprocessing.Process(target=consumer, args=(l,))

    p.start()
    c.start()

    p.join()
    c.join()
    print "done"

它创建一个进程和一个共享由manager创建的列表的线程。生产者在每隔i秒后追加到列表,而消费者每隔5秒将列表减半。我预计这些列表将是相同的,并且两者都将在其上运行。但我观察到,消费者和制作人只共享一次列表,下次列表分开时。

观察到的输出:

producer l: [0]
producer l: [0, 1]
producer l: [0, 1, 2]
consumer l: [1, 2]
producer l: [0, 1, 2, 3]
producer l: [0, 1, 2, 3, 4]
consumer l: [2]
producer l: [0, 1, 2, 3, 4, 5]
consumer l: [2]
consumer l: [2]
producer l: [0, 1, 2, 3, 4, 5, 6]
producer l: [0, 1, 2, 3, 4, 5, 6, 7]
producer l: [0, 1, 2, 3, 4, 5, 6, 7, 8]
producer l: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
done

应该做些什么,以便他们两个都使用相同的列表?

2 个答案:

答案 0 :(得分:0)

你的问题就在这一行:

    l = l[len(l)/2:]

看起来没问题,但它实际上做的是创建l的新副本并将l分配给这个新对象。这意味着变量在您的第一个消费者执行时出现分歧,而消费者中的l不再绑定到传递给它的l

考虑这个版本的消费者:

def consumer(l):
    counter = 0
    while counter < 20:
        time.sleep(5)
        n = len(l)/2
        for _ in range(0,n+1):
            try:
                l.pop()
            except IndexError:
                pass
        print "consumer l: {}".format(l)
        counter += 5

它不是很优雅,它可能不是你想要的,但我已经用一个列表操作方法(在这种情况下为pop)替换为l分配给新实例。只要您继续使用lpopappendinsert等列表管理方法修改remove,就可以了。

如果你将l分配给新的东西,你的制作人会遇到同样的问题,但你已经有l.append(),所以那部分没问题。

答案 1 :(得分:0)

我将程序更改为:

import multiprocessing
import threading
import time

def consumer(l):
    counter = 0
    while counter < 20:
        time.sleep(5)
        del l[0 : len(l)/2+1]                                                                                                                                                   
        print("consumer l address {}".format(hex(id(l))))
        print "consumer l: {}".format(l)
        counter += 5

def producer(l):
    for i in range(10):
        time.sleep(i)
        print("producer l address {}".format(hex(id(l))))
        l.append(i)
        print "producer l: {}".format(l)

if __name__=="__main__":
    mgr = multiprocessing.Manager()
    l = mgr.list()

    print("list address {}".format(hex(id(l))))
    p = multiprocessing.Process(target=producer, args=(l,))
    c = multiprocessing.Process(target=consumer, args=(l,))

    p.start()
    c.start()

    p.join()
    c.join()
    print "done"

请注意,我使用了del而不是创建新列表并将其分配给旧列表。我还在上面的代码中添加了地址打印语句。如果按原样运行它,您应该看到列表的地址在任何地方都保持不变,因为我们没有重新创建列表。如果您将第9行更改回l = l[len(l)/2:]并运行该程序,您应该注意到第一次操作后消费者和生产者的列表地址会发生变化。