ProcessPoolExecutor和ThreadPoolExecutor有什么区别?

时间:2018-08-13 18:50:15

标签: python python-3.x multithreading parallel-processing python-3.5

我阅读了试图获得基本理解的文档,但它仅显示ProcessPoolExecutor可以绕开Global Interpreter Lock,我认为这是锁定变量或函数以便并行处理的方法请勿同时更新其值。

我要寻找的是何时使用ProcessPoolExecutor和何时使用ThreadPoolExecutor,以及在使用每种方法时应牢记的重点!

2 个答案:

答案 0 :(得分:2)

ProcessPoolExecutor在其自己的子进程中运行每个工人。

ThreadPoolExecutor在主进程中的不同线程中运行每个工作程序。

全局解释器锁定(GIL)不仅锁定变量或函数;还锁定了全局变量。它锁定整个解释器。这意味着每个内置操作(包括listodicts[3]['spam'] = eggs之类的操作都是自动线程安全的。

但这也意味着,如果您的代码受CPU限制(也就是说,它花费时间进行计算,而不是等待网络响应),而不是将大部分时间花费在旨在发布的外部库中GIL(如NumPy),一次只能有一个线程拥有GIL。因此,如果您有4个线程,即使您有4个甚至16个内核,大多数情况下,其中3个将坐在那里等待GIL。因此,您的代码不会比之前快4倍,而是会变得更慢。

同样,对于受I / O约束的代码(例如,在一堆服务器上等待响应您发出的一堆HTTP请求),线程就可以了;这仅是受CPU约束的代码。

每个单独的子进程都有其自己的GIL,因此这个问题消失了-即使您的代码受CPU限制,使用4个子进程仍然可以使其运行速度几乎提高了4倍。

但是子进程不共享任何变量。通常,这是一件好事:您将值的(副本)作为函数的参数传入,并返回值的(副本),并且进程隔离保证您可以安全地进行此操作。但是有时(通常出于性能原因,有时还因为您传递的对象无法通过pickle复制),这是不可接受的,因此您要么需要使用线程,要么使用更复杂的对象multiprocessing模块中的显式共享数据包装器。

答案 1 :(得分:1)

ProcessPool用于处理CPU绑定的任务,因此您可以受益于多个CPU。

线程用于io绑定任务,因此您可以受益于io等待。