我已经在这里问了一个关于多处理内部多线程的问题,其结果很难理解,并产生了一个更流行的问题多线程V/s多处理。
我已经浏览了有关此的各种帖子,但没有一个明确回答选择哪个而不是另一个,甚至没有检查哪个最适合需要的方法。从大部分帖子中,我了解到多线程是 I/O 和多处理是 CPU 绑定的,但是当我在 CPU 绑定进程的情况下同时使用两者时,结果不利于可以盲目选择 I/O 的多线程和 CPU 绑定的多处理的假设。
与我的情况一样,由于进程受 CPU 限制,因此结果有利于多线程。我观察到,有时即使在 CPU 密集型进程中,多线程也比多处理领先。我正在寻找可以帮助我选择其中之一使用的方法?
以下是我的分析,我使用 Python 3.7.2 在我的英特尔 i7、第 8 代、8 核、16 GB 机器上运行多进程和多线程代码(也在 Python 3.8.2 上进行了测试(
定义所需的函数和变量import numpy as np
import time
import concurrent.futures
a = np.arange(100000000).reshape(100, 1000000)
def list_double_value(x):
y = []
for elem in x:
y.append(2 *elem)
return y
def double_value(x):
return 2* x
情况 1(使用函数列出输入并将其每个参数乘以 2
使用list_double_value功能进行多进程(耗时 145 秒(t = time.time()
with concurrent.futures.ProcessPoolExecutor() as executor:
my_results = executor.map(list_double_value, a) # takes a list and double its value
print(time.time()-t)
使用list_double_value函数进行多线程处理(耗时 28 秒(t = time.time()
with concurrent.futures.ThreadPoolExecutor() as executor:
my_results = executor.map(list_double_value, a)
print(time.time()-t)
情况 2(使用取值并将其乘以 2 的函数(
使用双精度值进行多重处理(耗时 2.73 秒(t = time.time()
with concurrent.futures.ProcessPoolExecutor() as executor:
my_results = executor.map(double_value, a)
print(time.time()-t)
使用双精度值的多线程(耗时 0.2660 秒(t = time.time()
with concurrent.futures.ThreadPoolExecutor() as executor:
my_results = executor.map(double_value, a)
print(time.time()-t)
从上面的分析来看,每次在为多线程或多处理编写代码之前,我们都需要检查哪个执行得更快并选择加入,或者是否有任何一组规则提供具体的规则来选择一个而不是另一个?
还要让我知道所有这些结果是否是由于我使用的lib concurrent.futures。(我也不确定库(
Python的性能和可伸缩性受到 Python 引擎深处称为全局解释器锁或 GIL 的机制的极大限制。这是一个复杂的主题,但简要说明 GIL 会阻止单个 python 进程充分利用多个 CPU。因此,当您使用多线程(一个进程中的多个线程(时,您不会看到拥有 2、4 或 8 个 CPU/内核的性能提升。
多处理是不同的。在多处理中,使用多个单独的 Python 进程(每个进程一个线程(,每个进程都有自己单独的 GIL。每个进程都可以在自己的CPU上运行,因此您的程序可以有效地使用更多的系统资源。
如果您需要极其轻量级的任务,则需要线程,因为在多进程方法中,每个进程都有一些开销 - 每个进程都是一个单独的 Python 解释器。某些类型的任务间通信需要线程。如果您没有这些需求,通常使用多处理方法会更好。
在多处理中执行多线程将是一种高级的、有点奇怪的方法。我认为你最好不要混合模式。
我建议使用 Dask,它可以让您以针对并行性进行优化的方式设置计算。Dask 支持多线程和多处理(以及多台机器(,因此您可以编写一次代码并尝试两种方式。
https://docs.dask.org/en/latest/