繁体   English   中英

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

[英]Does Python support multithreading? Can it speed up execution time?

我对多线程是否适用于 Python 感到有些困惑。

我知道有很多关于这个的问题,我已经阅读了很多,但我仍然感到困惑。 我从自己的经验中了解到,也看到其他人在 StackOverflow 上发布了他们自己的答案和示例,多线程确实可以在 Python 中实现。 那么为什么大家老是说Python被GIL锁住了,一次只能运行一个线程呢? 它显然有效。 或者我没有得到一些区别?

许多发帖者/受访者也不断提到线程是有限的,因为它不使用多核。 但我会说它们仍然很有用,因为它们同时工作,因此可以更快地完成组合工作量。 我的意思是,否则为什么会有一个 Python 线程模块?

更新:

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

我不完全确定这在实际中对我意味着什么,所以我只举一个我想要多线程的任务类型的例子。 例如,假设我想遍历一个很长的字符串列表,并且我想对每个列表项执行一些基本的字符串操作。 如果我拆分列表,将每个子列表发送到新线程中由我的循环/字符串代码处理,并将结果发送回队列,这些工作负载是否会大致同时运行? 最重要的是,这在理论上会加快运行脚本所需的时间吗?

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

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

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

GIL 所阻止的是使用多个 CPU 内核或单独的 CPU 来并行运行线程。

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

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

请注意,GIL 仅适用于 CPython 实现; Jython 和 IronPython 使用不同的线程实现(分别是本机 Java VM 和 .NET 公共运行时线程)。

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

Python 线程非常适合创建响应式 GUI,或处理多个短 Web 请求,其中 I/O 比 Python 代码更成为瓶颈。 它不适合并行化计算密集型 Python 代码,不适合此类任务的multiprocessing模块或委托给专用的外部库。

是的。 :)

您有低级线程模块和高级线程模块。 但是如果你只是想使用多核机器, 多处理模块是要走的路。

来自文档的引用:

在 CPython 中,由于全局解释器锁,一次只有一个线程可以执行 Python 代码(即使某些面向性能的库可能会克服这一限制)。 如果您希望您的应用程序更好地利用多核机器的计算资源,建议您使用多处理。 但是,如果您想同时运行多个 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}")

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM