Python 支持多线程吗? 它能加快执行时间吗?

对于多线程是否能在 Python 中工作,我有点困惑。

我知道关于这个问题有很多疑问,我也读过很多,但是我还是很困惑。根据我自己的经验,我知道在 Python 中确实可以实现多线程,并且看到其他人在 StackOverflow 上发布了自己的答案和示例。那么,为什么每个人都一直说 Python 被 GIL 锁定,而且一次只能运行一个线程呢?这显然是有效的。还是有什么我不明白的地方?

许多发布者/回复者也不断提到线程是有限的,因为它不使用多个核心。但我认为它们仍然有用,因为它们同时工作,因此可以更快地完成合并后的工作量。否则怎么会有 Python 线程模块呢?

更新:

谢谢你到目前为止所有的答案。我的理解是,多线程只能在某些 IO 任务中并行运行,但对于受 CPU 限制的多个核心任务,一次只能运行一个。

I'm not entirely sure what this means for me in practical terms, so I'll just give an example of the kind of task I'd like to multithread. For instance, let's say I want to loop through a very long list of strings and I want to do some basic string operations on each list item. If I split up the list, send each sublist to be processed by my loop/string code in a new thread, and send the results back in a queue, will these workloads run roughly at the same time? Most importantly will this theoretically speed up the time it takes to run the script?

另一个例子可能是,如果我可以渲染和保存四个不同的图片使用 PIL 在四个不同的线程,这是否比处理图片一个接一个更快?我想这个速度元件才是我真正想知道的,而不是正确的术语是什么。

我也知道多处理模块,但我现在的主要兴趣是中小型任务负载(10-30秒) ,因此我认为多线程将更为合适,因为子进程启动起来会很慢。

67698 次浏览

GIL 不会阻止线程化。GIL 所做的只是确保一次只有一个线程在执行 Python 代码; 控制仍然在线程之间切换。

GIL 防止的是使用多个 CPU 核或单独的 CPU 并行运行线程。

这只适用于 Python 代码。C 扩展可以并且确实发布了 GIL,允许多个 C 代码线程和一个 Python 线程跨多个核运行。这扩展到由内核控制的 I/O,例如 select()调用套接字读写,使 Python 在多线程多核设置中能够合理有效地处理网络事件。

然后许多服务器部署所做的,就是运行多个 Python 进程,让操作系统处理进程之间的调度,以最大限度地利用 CPU 核心。如果适合您的用例,您还可以使用 multiprocessing来处理来自一个代码库和父进程的多个进程之间的并行处理。

注意,GIL 只适用于 CPython 实现; Jython 和 IronPython 使用不同的线程实现(原生 JavaVM 和。NET 公共运行时线程)。

直接处理更新: 任何使用纯 Python 代码试图从并行执行中获得速度提升的任务,都不会看到速度提升,因为线程化的 Python 代码被锁定在一次执行的一个线程上。但是,如果混合使用 C 扩展和 I/O (比如 PIL 或 numpy 操作) ,任何 C 代码都可以与 活动 Python 线程并行运行。

Python 线程对于创建响应性 GUI 或处理多个短 Web 请求(I/O 比 Python 代码更容易成为瓶颈)非常有用。它不适合并行处理计算密集型的 Python 代码,对于这种任务,坚持使用 multiprocessing模块,或者委托给专用的外部库。

是的

您有低级的 线模块和高级的 threading模块。但是如果你只是想使用多核机器,多重处理模块就是最好的选择。

引自 医生:

在 CPython 中,由于 GIL,只有一个线程可以 立即执行 Python 代码(即使某些面向性能的 库可能会克服这个限制) 应用程序,以更好地利用计算资源 multi-core machines, you are advised to use multiprocessing. However, 线程仍然是一个适当的模型,如果您想运行多个 同时进行 I/O 限制任务。

Python 中允许线程化,唯一的问题是 GIL 将确保一次只执行一个线程(没有并行性)。

所以基本上,如果你想多线程的代码来加速计算它不会加速它只是一次执行一个线程,但如果你使用它与数据库交互,例如它会。

我同情这张海报,因为答案总是“这取决于你想做什么”。然而,在我的经验中,即使对于多处理来说,Python 中的并行加速也是非常糟糕的。

例如,查看这个教程(第二个顶部结果在谷歌) : https://www.machinelearningplus.com/python/parallel-processing-python/

我围绕这段代码设置了计时,并增加了池映射函数的进程数(2、4、8、16) ,得到了以下错误计时:

serial 70.8921644706279
parallel 93.49704207479954 tasks 2
parallel 56.02441442012787 tasks 4
parallel 51.026168536394835 tasks 8
parallel 39.18044807203114 tasks 16

密码: # 在开始时增加数组大小 # 我的计算节点有40个 CPU,所以这里有很多空闲

arr = np.random.randint(0, 10, size=[2000000, 600])
.... more code ....
tasks = [2,4,8,16]


for task in tasks:
tic = time.perf_counter()
pool = mp.Pool(task)


results = pool.map(howmany_within_range_rowonly, [row for row in data])


pool.close()
toc = time.perf_counter()
time1 = toc - tic
print(f"parallel {time1} tasks {task}")