我阅读文档试图获得基本的理解,但它只表明 ProcessPoolExecutor
允许回避 Global Interpreter Lock
我认为这是锁定的方式一个变量或函数,以便并行进程不会同时更新其值。
我正在寻找的是何时使用 ProcessPoolExecutor
以及何时使用 ThreadPoolExecutor
以及在使用每种方法时我应该记住什么!
最佳答案
ProcessPoolExecutor
在其单独的子进程中运行您的每个工作人员。
ThreadPoolExecutor
在主进程中的单独线程中运行您的每个 worker。
全局解释器锁 (GIL) 不只是锁定变量或函数;它锁定了整个解释器。这意味着每个内置操作,包括诸如 listodicts[3]['spam'] = eggs
之类的东西,都是自动线程安全的。
但这也意味着如果您的代码受 CPU 限制(也就是说,它把时间花在计算上而不是等待网络响应等),而不是将大部分时间花在旨在发布的外部库中GIL(如 NumPy),一次只有一个线程可以拥有 GIL。所以,如果你有 4 个线程,即使你有 4 个甚至 16 个内核,大多数时候,其中 3 个将等待 GIL。因此,您的代码并没有快 4 倍,而是变慢了一些。
同样,对于 I/O 绑定(bind)代码(例如,等待一堆服务器响应你发出的一堆 HTTP 请求),线程就很好;这仅适用于 CPU 绑定(bind)代码,这是一个问题。
每个单独的子进程都有自己单独的 GIL,因此这个问题就消失了——即使您的代码受 CPU 限制,使用 4 个子进程仍然可以使它的运行速度几乎提高 4 倍。
但是子进程不共享任何变量。通常,这是一件好事——您将值(的副本)作为参数传递给您的函数,然后返回值(的副本),并且进程隔离保证您可以安全地执行此操作。但偶尔(通常是出于性能原因,但有时也是因为您传递的对象无法通过 pickle
复制),这是 Not Acceptable ,因此您要么需要使用线程,要么使用multiprocessing
模块中更复杂的显式共享数据包装器。
关于python - ProcessPoolExecutor 和 ThreadPoolExecutor 有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51828790/