Python线程全部在单核上执行
我有一个Python程序,它会创建很多线程,同时运行4个线程,每个线程都在做一些耗时的操作。大概的代码逻辑是这样的:
for object in list:
t = Thread(target=process, args=(object))
# if fewer than 4 threads are currently running, t.start(). Otherwise, add t to queue
但是当我运行这个程序时,OS X的活动监视器显示4个逻辑核心中有1个的使用率达到了100%,而其他的几乎没有使用。显然,我不能强迫操作系统做什么,但我之前从来没有关注过多线程代码的性能,所以我在想是不是我漏掉了什么或者理解错了什么。
谢谢。
3 个回答
据我所知,在CPython中,全局解释器锁(Global Interpreter Lock,简称GIL)意味着在任何时刻只能有一个块的Python代码在运行。虽然在单处理器或单核心的机器上这并不会有什么影响,但在多核心的机器上,这就意味着实际上只能有一个线程在运行,导致其他核心都处于闲置状态。
Python有一个叫做全局解释器锁(Global Interpreter Lock,简称GIL)的东西,这个锁会阻止多个线程同时处理解释过的代码。
http://en.wikipedia.org/wiki/Global_Interpreter_Lock
http://wiki.python.org/moin/GlobalInterpreterLock
如果你想绕过这个限制,可以试试 多进程模块,这里有相关的建议:
要注意,在很多情况下(几乎所有你的“耗时操作”是用Python实现的计算的情况),多个线程实际上不会同时运行,这主要是因为Python有个叫做全局解释器锁(GIL)的东西。
GIL是一个解释器级别的锁。 这个锁阻止了在Python解释器中 多个线程同时执行。每个想要 运行的线程都必须等其他线程 释放GIL,这就意味着你的多线程 Python应用实际上是单线程的,对吧?是的,但也不完全是。 可以说是这样。
CPython在后台使用的是“操作系统”线程, 也就是说,每次请求创建新线程时, 解释器实际上会调用操作系统的 库和内核来生成一个新线程。这 和Java是一样的。所以在内存中, 你确实有多个线程,通常操作系统 会控制哪个线程被安排运行。在 多处理器的机器上,这意味着你 可以有很多线程分布在多个处理器上, 都在忙着做工作。
然而,虽然CPython确实使用 操作系统线程(理论上允许多个线程 在解释器中同时执行),但解释器 也强制一个线程在访问解释器和 栈之前必须先获得GIL,这样才能 随意修改内存中的Python对象。 后面这一点就是GIL存在的原因: GIL防止多个线程同时访问Python对象。 但这并不意味着你就可以高枕无忧 (正如银行示例所示),你仍然需要 注意锁的使用;你并没有得到免费的 通行证。GIL是为了保护解释器的内存, 而不是为了保护你的心理健康。
想了解更多细节,可以查看Jesse Noller的文章中关于全局解释器锁的部分。
要解决这个问题,可以看看Python的多进程模块。
使用多个进程(合理使用IPC)是[...]在多CPU机器上编写应用程序时,比线程更好的方法。
-- Guido van Rossum(Python的创造者)
根据@spinkus的评论进行编辑:
如果Python不能同时运行多个线程,那为什么还要有线程呢?
在Python中,线程仍然可以在进行不需要修改解释器状态的同时操作时非常有用。这包括许多(大多数?)长时间运行的函数调用,这些调用不是在Python内部进行的计算,比如I/O(文件访问或网络请求)和[对Numpy数组的计算][6]。这些操作在等待结果时会释放GIL,从而允许程序继续执行。然后,一旦结果返回,线程必须重新获取GIL才能在“Python环境”中使用该结果。