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

Python支持多线程吗?它能加快执行时间吗?,python,multithreading,Python,Multithreading,我对多线程是否在Python中工作有点困惑 我知道有很多关于这方面的问题,我读过很多,但我仍然感到困惑。根据我自己的经验,我知道并且看到其他人在StackOverflow上发布了他们自己的答案和示例,在Python中多线程确实是可能的。那么,为什么每个人都说Python被GIL锁定,一次只能运行一个线程?它显然起作用了。还是有什么区别我不明白 许多海报/受访者还不断提到线程是有限的,因为它不使用多核。但我想说,它们仍然很有用,因为它们同时工作,因此可以更快地完成合并的工作负载。我的意思是,为什么

我对多线程是否在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核心。如果适合您的用例,您还可以使用来处理来自一个代码库和父进程的多个进程之间的并行处理

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

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

Python线程非常适合创建响应性GUI,或者处理多个短web请求,其中I/O比Python代码更是瓶颈。它不适用于并行计算密集型Python代码,对于此类任务,请坚持使用多处理模块,或委托给专用的外部库。

是:

您有低级模块和高级模块。但如果你只是想使用多核机器,那么模块就是一条出路

引自:

在CPython中,由于全局解释器锁,只有一个线程可以 立即执行Python代码,即使某些面向性能 图书馆可以克服这一限制。如果你想要你的 应用程序以更好地利用 多核机器,建议您使用多处理。然而 如果要运行多个线程,线程仍然是合适的模型 同时执行I/O绑定任务


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

因此,基本上,如果你想多线程的代码来加速计算,它不会加快它的速度,因为只是在
e线程一次执行一次,但如果您使用它与数据库交互,例如,它将执行

我喜欢海报,因为答案总是取决于你想做什么。然而,根据我的经验,python中的并行加速一直都很糟糕,即使对于多处理也是如此

例如,在google中查看本教程的排名第二的结果:

我对这段代码进行了计时,并增加了池映射函数的进程数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}")

这是一个相当复杂的问题。我认为答案在于你想让线程做什么。在大多数情况下,GIL会阻止多个线程同时运行。但是,也有一些情况下会发布GIL,例如从文件中读取,以便并行执行。还要注意,GIL是Cpython最常见实现的一个实现细节。pythonjython、pypypy等的其他实现都没有GILAFAIK@mgilsonPyPy有一个GIL。@delnan-你似乎是对的。谢谢。子流程的启动可能会很慢-您可以创建一个任务池来准备执行。开销可能被限制在序列化/反序列化任务开始工作所需的数据所需的大致时间内。@KarimBahgat,这正是我的意思。谢谢@MartijnPieters,那么我就可以更清楚地回答我的问题:线程是否可以用来加速代码,例如for循环,这是否定的。也许你或其他人可以写一个我可以接受的新答案,提供一些通用模块/代码/操作的具体示例,其中GIL允许线程并行运行,因此速度更快,例如前面提到的I/O和网络/套接字读取操作的示例,以及在Python中使用多线程的任何其他情况。如果可能的话,也许有一个很好的常见多线程使用列表和一些编程示例?不,我不认为这样的答案会很有帮助;老实说你永远无法创建一个详尽的列表,但经验法则是,任何I/O文件读写、网络套接字、管道都是用C处理的,很多C库也会为它们的操作发布GIL,但这要由库来为你记录。我的错,直到现在才看到你更新的答案,在这里,您给出了一些很好的线程使用示例。其中包括:如果我的网络编程错误,请纠正我,例如urllib.urlopen?,从Python GUI中调用一个Python脚本,以及使用线程调用多个PIL eg Image.transform和numpy eg numpy.array操作。您在评论中还提供了一些示例,例如使用多个线程读取文件,例如f.read?。我知道一个详尽的列表是不可能的,只是想要你在更新中给出的例子类型。不管怎样,我都接受了你的答案:@KarimBahgat:是的,urllib.urlopen会调用网络套接字,等待套接字I/O是切换线程和做其他事情的绝佳机会。虽然这与这个问题没有直接关系,但值得注意的是,有时线程根本与性能无关;将代码编写为多个独立的执行线程可能更简单。例如,您可能有一个线程播放背景音乐,一个线程为用户界面服务,另一个线程在最后必须完成的计算上嘎嘎作响,但并不匆忙。尝试使用UI runloop来顺序播放下一个音频缓冲区,或者将计算分解为足够小的部分以不干扰交互,这可能比使用线程困难得多。