为什么多处理的apply_async如此挑剔?

时间:2015-04-12 04:21:10

标签: python multithreading multiprocessing python-multithreading gil

无问题的示例代码:

from multiprocessing import *
import time
import random

def myfunc(d):
    a = random.randint(0,1000)
    d[a] = a
    print("Process; %s" % a)

print("Starting mass threads")

man = Manager()
d = man.dict()
p = Pool(processes=8)

for i in range(0,100):
    p.apply_async(myfunc, [d])

p.close()
p.join()

print(d)

print("Ending multiprocessing")

如果您将p.apply_async(myfunc, [d])更改为p.apply_async(myfunc, (d))p.apply_async(myfunc, d),则该池将无法正常工作。如果您向myfunc添加另一个arg然后只是传入None它就会像p.apply_async(myfunc, (None, d))一样工作 - 但为什么?

1 个答案:

答案 0 :(得分:3)

apply_async的文档说明如下:

  

apply(func[, args[, kwds]])

     

使用参数func和关键字参数args调用kwds。它会阻塞,直到结果准备就绪。鉴于此块,apply_async()更适合并行执行工作。此外,func仅在池中的一个工作程序中执行。

因此,它不是采用星型和双星型参数,而是将位置参数和关键字参数作为函数的第2和第3个参数传递给目标函数;第二个必须是 iterable ,第三个必须是映射


请注意,由于apply是异步工作的,因此除非您从结果中.wait.get,否则您不会看到任何例外情况;

您可以尝试简单地说:

for i in range(0,100):
    result = p.apply_async(myfunc, d)

print(result.get())

在上面的代码中,result.get()等待第100个线程的完成并返回其返回的值 - 或尝试因为它将失败,因为托管字典不能用作位置参数:

Traceback (most recent call last):
  File "test.py", line 21, in <module>
    print(result.get())
  File "/usr/lib/pythonN.N/multiprocessing/pool.py", line 558, in get
    raise self._value
KeyError: 0

因此,查看原始问题:请注意[d]是长度为1的列表; (d)d相同;要拥有长度为1的元组,您需要输入(d,)。来自Python 3 tutorial section 5.3

  

一个特殊问题是构造包含0或1的元组   items:语法有一些额外的怪癖来容纳这些。空   元组是由一对空括号构成的;一个元组   通过使用逗号跟随值来构造一个项目(它不是   足以将一个值括在括号中)。丑,但是   有效。例如:

>>> empty = ()
>>> singleton = 'hello',    # <-- note trailing comma
>>> len(empty)
0
>>> len(singleton) 
1
>>> singleton 
('hello',)

(d,)[d]{d},甚至iter(frozenset(d)){d: True}可以很好地作为您的位置参数;所有这些args将导致一个Iterable,其迭代器只产生1个值 - d。另一方面,如果你已经传递了几乎任何其他类型的值而不是那个不幸的托管字典,那么你会得到一个更有用的错误;如果值是42,你就得到了:

TypeError: myfunc() argument after * must be a sequence, not int