Python 并发代码中的死锁

Python 并发代码中的死锁,python,multiprocessing,concurrent.futures,Python,Multiprocessing,Concurrent.futures,我一直在尝试使用concurrent.futures.ProcessPoolExecutor对一些代码进行并行化,但一直存在ThreadPoolExecutor不会出现的奇怪死锁。一个简单的例子: from concurrent import futures def test(): pass with futures.ProcessPoolExecutor(4) as executor: for i in range(100): print('submitti

我一直在尝试使用
concurrent.futures.ProcessPoolExecutor
对一些代码进行并行化,但一直存在
ThreadPoolExecutor
不会出现的奇怪死锁。一个简单的例子:

from concurrent import futures

def test():
    pass

with futures.ProcessPoolExecutor(4) as executor:
    for i in range(100):
        print('submitting {}'.format(i))
        executor.submit(test)
在Python3.2.2(在64位Ubuntu上)中,提交所有作业后,这种情况似乎会一直挂起,而且每当提交的作业数量大于工作人员数量时,这种情况就会发生。如果我将
ProcessPoolExecutor
替换为
ThreadPoolExecutor
,它将完美地工作

为了进行调查,我给了每个将来的用户一个回调,以打印
I
的值:

from concurrent import futures

def test():
    pass

with futures.ProcessPoolExecutor(4) as executor:
    for i in range(100):
        print('submitting {}'.format(i))
        future = executor.submit(test)

        def callback(f):
            print('callback {}'.format(i))
        future.add_done_callback(callback)
这让我更加困惑,
callback
打印出来的
i
的值是调用它时的值,而不是定义它时的值(因此我从未看到
callback0
,但我得到了很多
callback99
s)。同样,
ThreadPoolExecutor
打印出预期值

想知道这是否是一个bug,我尝试了python的最新开发版本。现在,代码似乎至少终止了,但我仍然打印出了错误的
I

那么,谁能解释一下:

  • 在Python3.2和显然修复了此死锁的当前开发版本之间,
    ProcessPoolExecutor
    发生了什么

  • 为什么打印
    i
    的“错误”值

编辑:正如jukiewicz在下面指出的,当然打印
i
会在调用回调时打印值,我不知道我在想什么。。。如果我将值为
I
的可调用对象作为其属性之一传递给它,那么它将按预期工作


编辑:更多一点信息:所有回调都已执行,因此看起来好像是
executor.shutdown
(由
executor.\uuuu exit\uuuu
调用)无法判断进程是否已完成。在当前的Python3.3中,这似乎是完全固定的,但是,
多处理
并发.futures
似乎有很多变化,所以我不知道是什么解决了这一问题。由于我不能使用3.3(它似乎与numpy的发行版或开发版本都不兼容),我尝试将其多处理和并发软件包复制到我的3.2安装中,这似乎很好。尽管如此,就我所见,
ProcessPoolExecutor
在最新版本中被完全破坏,但没有其他人受到影响,这似乎有点奇怪。

我对代码进行了如下修改,解决了这两个问题
callback
函数被定义为闭包,因此每次都会使用更新后的
i
值。至于死锁,这可能是在所有任务完成之前关闭执行器的原因。等待未来完成也解决了这一问题

from concurrent import futures

def test(i):
    return i

def callback(f):
    print('callback {}'.format(f.result()))


with futures.ProcessPoolExecutor(4) as executor:
    fs = []
    for i in range(100):
        print('submitting {}'.format(i))
        future = executor.submit(test, i)
        future.add_done_callback(callback)
        fs.append(future)

    for _ in futures.as_completed(fs): pass

更新:哦,对不起,我还没有读你的更新,这似乎已经解决了。

至于第二个问题,处理print
99是很自然的。symbol
i
受全局上下文的约束,创建新进程的成本很高,所以当你开始执行任何事情时,
i==99
。另外,我有Ubuntu 64位,Python 3.2.2,第一个代码段没有挂起…@julkiewicz:这很奇怪。我刚刚在另一台运行64位Scientific Linux和python 3.2.2的机器上试用了它,在10次试用中打印了10次“提交99”后,它就停止了。我甚至尝试过在
中包装代码,如果我听说在Windows上进行多处理是必要的,那么就用
来包装代码。事实上,我看不到发生这种情况所需的作业和工人数量的模式。例如,1个工人和2个工作是可以的,1个工人和3个工作挂起,8个工人和16个工作挂起大约一半的时间。