为什么python的多处理池get()函数有时会被阻止,而其他人却不会?

时间:2018-11-21 17:15:11

标签: python multiprocessing

我在python中使用Multiprocessing.Pool,但我不了解发生了什么。这是一个虚拟版本,显示了我的意思:

from multiprocessing import Pool
from time import sleep

def f1():
    for i in range(5):
        sleep(.1)
        print("f1:",i)
    print("f1 exiting")
    return('f1f1f1f1f1f1f1')

def f2():
    for i in range(10):
        sleep(.1)
        print("f2:",i)
    print("f2 exiting")
    return('f2f2f2ff2f2f2f2f2f')

pool = Pool(processes=2)

print('starting apply_async for p1, p2')
p1 = pool.apply_async(f1)
p2 = pool.apply_async(f2)
print('finished apply_async for p1, p2')


print('starting get() for p1, p2')
print(p1.get(timeout=10))
print(p2.get(timeout=10))
print('finished get() for p1, p2')


print('\n\n\ndone')

如果我运行此命令,则f1和f2同时运行并输出:

starting apply_async for p1, p2
finished apply_async for p1, p2
starting get() for p1, p2
f1: 0
f2: 0
f2: 1
f1: 1
f2: 2
f1: 2
f2: 3
f1: 3
f1: 4
f2: 4
f1 exiting
f1f1f1f1f1f1f1
f2: 5
f2: 6
f2: 7
f2: 8
f2: 9
f2 exiting
f2f2f2ff2f2f2f2f2f
finished get() for p1, p2

因此很显然,当p1调用get()时,它不会阻止程序其余部分的执行,而是立即转到p2.get()。

但是,如果我改为这样做(请注意,f1略有更改):

from multiprocessing import Pool
from time import sleep

def f1():
    for i in range(5):
        sleep(1)
        print("f1:",i)
    print("f1 exiting")
    return('f1f1f1f1f1f1f1')

def f2():
    for i in range(10):
        sleep(.1)
        print("f2:",i)
    print("f2 exiting")
    return('f2f2f2ff2f2f2f2f2f')


pool = Pool(processes=1)

print('starting apply_async for p1')
p1 = pool.apply_async(f1)
print('finished apply_async for p1')

print('starting get() for p1')
print(p1.get(timeout=10))
print('finished get() for p1')

print('calling f2()')
f2()

print('\n\n\ndone')

我得到:

starting apply_async for p1
finished apply_async for p1
starting get() for p1
f1: 0
f1: 1
f1: 2
f1: 3
f1: 4
f1 exiting
f1f1f1f1f1f1f1
finished get() for p1
calling f2()
f2: 0
f2: 1
f2: 2
f2: 3
f2: 4
f2: 5
f2: 6
f2: 7
f2: 8
f2: 9
f2 exiting

因此,在这种情况下,p1.get()对程序的主要部分阻塞。在这种情况下,如果我使用1或2个进程,也没有什么区别。

我知道这是因为在这种情况下,没有与Pool工作人员之一一起调用f2,但是我仍然感到困惑。甚至更奇怪的是,如果我在第二种情况下切换f1和f2的顺序,例如:

pool = Pool(processes=1)

print('starting apply_async for p1')
p1 = pool.apply_async(f1)
print('finished apply_async for p1')

print('calling f2()')
f2()

print('starting get() for p1')
print(p1.get(timeout=10))
print('finished get() for p1')

它确实在f2仍在执行时启动f1的get():

starting apply_async for p1
finished apply_async for p1
calling f2()
f2: 0
f2: 1
f2: 2
f2: 3
f2: 4
f2: 5
f2: 6
f2: 7
f2: 8
f1: 0
f2: 9
f2 exiting
starting get() for p1
f1: 1
f1: 2
f1: 3
f1: 4
f1 exiting
f1f1f1f1f1f1f1
finished get() for p1

(您可以在f2:8和f2:9之间看到f1:0。)

这真让我感到困惑。在这种情况下,f2与Pool的内容无关,那么它在第一次被调用时又如何不阻塞?

有人可以弄清Pool发生了什么吗?我已经阅读了文档,但并没有真正为我清除。

1 个答案:

答案 0 :(得分:1)

在每种情况下它都处于阻塞状态。与第二个示例和第三个示例与第一个示例的不同之处在于,您没有打印出p1.getp2.get之间的任何内容,因此无法从打印输出中查看是否阻塞。 p2会在您调用apply_async(f2)后立即开始运行,因此为什么您在p2仍在等待的同时从p1获取输出,但这与您对{ p1.get