python:multiprocessing.dummy使用imap_unordered通过增长/修改列表进行迭代

时间:2015-10-13 17:19:40

标签: python list loops python-multithreading pool

摘要

  • 我想使用多个线程来处理列表。
  • 在处理列表时,在某些情况下,可以添加列表,以便也应该处理新的添加。 (它不会是无限的)
  • 来自此处理的输出可能很大,并且只会迭代一次。我没有理由把整件事留在记忆中。
  • 输出可以/应该立即处理。订购无关紧要。

代码
测试我写的以下内容,在Windows中使用python 2.7.10:

from multiprocessing.dummy import Pool as thPool
from time import sleep

def funct(n):
    #print "{"+str(n)+"}",
    #print "|"+str(len(li))+"|",
    if n % 2 == 0:
        return n
    elif n % 3 == 0:
        li.append(12)
    elif n % 5 == 0:
        li.append(14)
    #sleep(.25)

if __name__ == "__main__":
    P = thPool(4)
    li = [1,2,3,4,5,6,7,8,9]

    lf=(i for i in P.imap_unordered(funct,li) if i is not None)
    #print lf
    for m in lf:
        print "("+str(m)+")",
        #sleep(.25)

我所期望的是,没有特别的顺序,偶数(2,4,6,8),两个12,一个14。

结果
我多次跑了以上。我每次都得到不同的结果:

  • (2)(4)(6)(8)(12)(14)(12)
  • (2)(4)(6)(8)(12)(14)
  • (2)(4)(6)(8)

我假设迭代器在附加列表之前完成。我在 funct 中放置了一个调试打印语句来显示全局列表 li 的大小,我得到了:

  • | 9 || 9 | | 9 || 9 | | 10 || 10 | | 10 | (2)| 11 || 11 | (4)(6)(8)
  • | 9 || 9 | | 9 | | 9 || 9 | | 11 |(2)| 11 | | 11 || 11 | | 11 | | 12 | (4)(6)(8)(12)(14)
  • | 9 || 9 | | 9 || 9 | | 9 | | 10 | (2)| 11 | (4)| 11 | (6)| 11 | | 12 |(8)| 12 | | 12 |(12)(14)(12)

在竞争激烈的情况下,我也开始制造延迟,但这似乎对可预测性没有影响。

问题

  1. 为什么结果不可预测?如何添加列表有时未处理? (即使函数内的列表大小> 9,表示已附加列表)
  2. 为什么输出始终有序?
  3. 是否有使用multiprocessing.dummy池功能的正确方法?
  4. 是否有建议的替代方法?

2 个答案:

答案 0 :(得分:1)

编辑:我更清楚地重新阅读问题,并了解您想要动态修改列表。这需要使用线程安全的适当共享对象(例如Array,Queue或甚至来自多处理库的Manager)来完成。

另外,遗憾的是,在这种情况下,我认为你不能使用imap_unordered()。我认为您看到的行为是由于imap_unordered有时会到达可迭代的末尾,并且在之前将其他项目放在列表中之前停止分发

答案 1 :(得分:1)

在这种情况下,请勿使用multiprocessing.Pool.imap_unordered。有办法使它发挥作用,但它们是丑陋和脆弱的。使用生产者 - 消费者模式,消费者偶尔会充当生产者。

from multiprocessing.dummy import Process, Queue


def process(inq, outq):
    while True:
        n = inq.get()
        try:
            if n % 2 == 0:
                outq.put(n)  # Queue up for printing
            elif n % 3 == 0:
                inq.put(12)  # Queue up for future processing
            elif n % 5 == 0:
                inq.put(14)  # Queue up for future processing
        finally:
            inq.task_done()

def printer(q):
    while True:
        m = q.get()
        try:
            print "("+str(m)+")",
        finally:
            q.task_done()

def main():
    workqueue = Queue()
    printqueue = Queue()
    printworker = Process(target=printer, args=(printqueue,))
    printworker.daemon = True
    printworker.start()

    for i in range(4):
        processor = Process(target=process, args=(workqueue, printqueue))
        processor.daemon = True
        processor.start()

    li = [1,2,3,4,5,6,7,8,9]

    for x in li:
        workqueue.put(x)  # optionally, put all items before starting processor threads so initial work is processed strictly before generated work
    workqueue.join()  # Wait for all work, including new work, to be processed
    printqueue.join()  # Then wait for all the results to be printed

if __name__ == '__main__':
    main()