如何使用Python multiprocessing.Pool实现Java FixedThreadPool

时间:2013-10-11 17:25:28

标签: python python-2.7 multiprocessing

我需要在Python中使用进程池。要求如下:

池的固定大小为10.我有许多工作要提交到池中(N> 10)。 在Java中,可以使用FixedThreadPool来实现此目的。提交作业,一旦线程完成任务,客户端就可以提交下一个任务。因此,如果当前正在运行10个任务,则客户端无法提交第11个任务。但如果一个完成,客户端就能够将下一个任务提交给可用的线程。

这是我用来测试一些想法的代码:

import multiprocessing, time


def printStuff(number):
    print number
    if number % 2 : time.sleep(0.5)
    return number*number

pool = multiprocessing.Pool(5, None, None, None)   
a = []

def execute():
    def resultAggregator(n):
        print 'aggregator called...'
        a.append(n)
    for i in range (0, 34):

        # With callback
        #pool.apply_async(printStuff, [i], None, resultAggregator)
        #print "called for ", i

        # Without callback
        res = pool.apply_async(printStuff, [i])
        print "called for" , i, "returned ", res.get()

    pool.close() # disable sumitting any more tasks
    pool.join() # wait for all the worker to finish

execute()
print a

res.get()阻塞,直到printStuff返回。使用回调变体甚至不会调用printStuff。请注意,在这两种情况下,a末尾都是空的。

如何实现上述行为的任何想法?代码片段会很棒,但它足以指向我不知道的现有库函数,或只是抛出一些想法。

1 个答案:

答案 0 :(得分:2)

我不了解Java FixedThreadPool,但我可以修复您的代码; - )

你显然不想使用res.get(),对吧?所以我会忽略那一部分。 .apply_async()的问题在于您没有正确调用它。我很惊讶也没有例外!参数列表应该是元组,而不是列表(对于内置apply()函数)。对于keyword-args参数,None不起作用。如果您没有要传递的关键字参数,请将其保留(如下所示)或传递空字典({})。

此处的其他更改更具外观:引入了IO锁以防止终端输出被加扰,并为了清晰起见引入了__name__ == "__main__"检查,以便代码也将在Windows上运行:

import multiprocessing, time

def getlock(lck):
    global iolock
    iolock = lck

def printStuff(number):
    with iolock:
        print number
    if number % 2:
        time.sleep(0.5)
    return number*number

def execute():
    def resultAggregator(n):
        with iolock:
            print 'aggregator called...'
        a.append(n)

    for i in range(34):
        pool.apply_async(printStuff, (i,), callback=resultAggregator)
        with iolock:
            print "called for ", i

if __name__ == "__main__":
    a = []
    iolock = multiprocessing.Lock()
    pool = multiprocessing.Pool(5, getlock, (iolock,))   
    execute()
    pool.close()
    pool.join()
    print a

后来:错误

如果为关键字参数传递None,则实际上会引发异常 - 但multiprocessing会对其进行抑制。唉,这是异步噱头的一个常见问题:没有好办法提出异常!它们发生在与您的主程序"无关的环境中。恰巧当时正在做。

至少Python 3.3.2的.apply_async()实现也有一个可选的error_callback参数。我不知道它何时推出。如果您提供它,则会向其传递异步异常,因此您可以决定如何报告(或记录或忽略...)它们。添加此功能:

def ouch(e):
    raise e

并将调用更改为:

pool.apply_async(printStuff, (i,), None, resultAggregator, ouch)

使用此异常详细信息生成以ouch()结尾的回溯:

TypeError: printStuff() argument after ** must be a mapping, not NoneType

所以,至少,使用最新的Python,你可以安排不让异步错误无形地通过。

Q&安培; A

  

你能解释一下"全球iolock" getLock()中的声明?我想到了   为每个子进程定义一个全局变量,但是从中更改名称   iolock到iiolock? "主"使iolock对工作进程不了解。

抱歉,我无法从完全告诉您所做的事情。名称iolock旨在成为所有进程(主要和子进程)中的全局。这是因为我的代码中的所有流程都使用名称iolock

例如,如果"通过更​​改名称..."你的意思是你只是替换了

iolock = multiprocessing.Lock()

iiolock = multiprocessing.Lock()

然后你会有一个例外:

Traceback (most recent call last):
  ...
    pool = multiprocessing.Pool(5, getlock, (iolock,))
NameError: global name 'iolock' is not defined

如果您也将该行(pool = ...)更改为使用iiolock,那么当主进程中的resultAggregator尝试使用{{1}时,您会遇到另外的异常}}:

iolock

所以我不知道你到底做了什么。

  

在execute中声明printStuff也会导致静默   错误(代码不会超过打印进度"要求")

那不行。未声明Python中的函数 - Exception in thread Thread-3: Traceback (most recent call last): ... File "mpool.py", line 19, in resultAggregator with iolock: NameError: global name 'iolock' is not defined 可执行语句。在def执行之前,printStuff的代码不存在。由于只有主程序执行def printstuff,因此execute()内的函数def仅存在于主程序中。

确实如此
execute()

pool.apply_async(printStuff, (i,), callback=resultAggregator) 传递给子进程,但所有传递的东西都通过发送端的pickling和接收端的unpickling工作,并且不能对函数对象进行pickle。你确定没有这样的错误吗?:

printStuff

(我在这里使用Python 3 - 也许它在Python 2下有所不同。)

在任何情况下,Python都不是Java - 不要为嵌套而疯狂 - 保持简单;-)子进程使用的每个函数和类都应该在模块级别定义({{1在Python中也是一个可执行语句!Python中唯一的#34;声明&#34;是_pickle.PicklingError: Can't pickle <class 'function'>: attribute lookup builtins.function failed class语句。

更多Q&amp; A

  

你对这些假设是正确的。我改为iiolock   主要的地方

仍然不知道完全你做了什么。对于这样的事情,你真的必须发布代码,而不仅仅是描述你做了什么。我只能猜测 - 这真的很痛苦;-)怎么样:如果你有新问题,请打开一个新问题?

根据您在此处描述的内容(&#34;除了主要&#34之外的所有地方;),您将在global中获得异常,因为新的nonlocal名称不会存在于execute()中主进程(这是iiolock运行的唯一进程 - 并且您说更改了execute()中的旧iolock。但是你没有提到异常,所以我猜测你并没有真正按照你所说的去做(&#34;除了主要#34之外的所有地方)。

  

并且期待新进程获得相同的锁定   作为参数传递给初始化函数但每个都有它   拥有全球的iiolock变量。多个进程如何共享   无论如何都是相同的变量(不是内存内容不同   对于每个过程???)。

这有两个答案;-)最直接相关的一个是main()(在我的原始代码中 - 我真的不知道你的代码现在是什么样的)是由{{1创建的对象(它是一个iolock)并通过multiprocessing传递给子进程:

mp.Lock()

mp.Pool()控制着这里的所有内容,并在全世界范围内开展工作,以确保此pool = multiprocessing.Pool(5, getlock, (iolock,)) ^^^^^^ 在整个流程中具有一致的状态。它不仅仅是任何旧变量,而是mp知道mp.Lock()所有行为的所有内容。

到目前为止,第二个答案不适用于此问题中的任何代码,您还可以在&#34;共享内存和#34;中创建某些类型的数据。使用mp。请参阅mpmp以及mp.Value的文档。这些值是跨进程真正(物理)共享的。

但除了那些(通过 mp.Array实现了的对象,以及从multiprocessing.sharedctypes获得的&#34;共享内存&#34;)之外,你是对的:跨进程不共享任何其他值(在物理上或语义上)。通过在各个mp同步点进行酸洗(序列化)和取消排序(从pickle字符串重建对象的值)来完成所有其他类型的对象值的通信(就像当你mp一个对象时在mp上,另一个进程.put()。)