我一直在尝试使用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)
在python 3.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)
这让我更加困惑 - i
打印出来的callback
的值是调用它时的值,而不是它定义时的值(所以我从未看到{ {1}}但我得到了很多callback 0
s。同样,callback 99
打印出预期值。
想知道这可能是一个bug,我尝试了python的最新开发版本。现在,代码至少似乎终止了,但我仍然打印出错误的ThreadPoolExecutor
值。
所以任何人都可以解释:
在python 3.2和显然解决这个死锁的当前开发版本之间i
发生了什么
为什么要打印ProcessPoolExecutor
的'错误'值
i
将在调用回调时打印值,我不知道我在想什么...如果我传递一个可调用的对象i
的值作为其属性之一,按预期工作。
编辑:更多信息:所有回调都被执行,因此看起来i
(由executor.shutdown
调用)无法判断进程是否已完成。这似乎在当前的python 3.3中完全修复了,但似乎对executor.__exit__
和multiprocessing
进行了很多更改,所以我不知道是什么修复了这个。由于我不能使用3.3(它似乎与numpy的发行版或开发版本兼容),我尝试简单地将其多处理和并发包复制到我的3.2安装,这似乎工作正常。不过,看起来有点奇怪 - 据我所知 - concurrent.futures
在最新版本中完全被破坏了,但没有其他人受到影响。
答案 0 :(得分:2)
我修改了如下代码,解决了这两个问题。 callback
函数被定义为闭包,因此每次都会使用i
的更新值。至于死锁,这可能是在所有任务完成之前关闭Executor的原因。等待期货完成也解决了这个问题。
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
更新:哦,对不起,我还没看过您的更新,这似乎已经解决了。