Python 线程全部在单个核心上执行
- 2025-03-06 08:55:00
- admin 原创
- 74
问题描述:
我有一个 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%,而其他核心接近 0。显然我无法强迫操作系统做任何事情,但我以前从来没有关注过这样的多线程代码的性能,所以我想知道我是否只是遗漏了或者误解了什么。
谢谢。
解决方案 1:
请注意,在许多情况下(几乎所有“昂贵操作”都是用 Python 实现的计算的情况),由于 Python 的全局解释器锁(GIL),多个线程实际上不会同时运行。
GIL 是解释器级锁。此锁可防止在 Python 解释器中同时执行多个线程。每个想要运行的线程都必须等待其他线程释放 GIL,这意味着您的多线程 Python 应用程序本质上是单线程的,对吗?是的。不完全是。有点。
CPython 在幕后使用所谓的“操作系统”线程,也就是说,每次发出创建新线程的请求时,解释器实际上都会调用操作系统的库和内核来生成新线程。例如,这与 Java 相同。因此,在内存中您确实有多个线程,并且操作系统通常控制要安排运行哪个线程。在多处理器机器上,这意味着您可以让多个线程分布在多个处理器上,所有线程都愉快地工作。
然而,虽然 CPython 确实使用了操作系统线程(理论上允许多个线程同时在解释器中执行),但解释器也会强制线程获取 GIL,然后它才能访问解释器和堆栈,并且可以随意修改内存中的 Python 对象。后一点是 GIL 存在的原因:GIL 可防止多个线程同时访问 Python 对象。但这并不能让您(如 Bank 示例所示)免于成为锁敏感生物;您不会获得免费服务。GIL 的存在是为了保护解释器的内存,而不是您的理智。
有关更多详细信息,请参阅Jesse Noller 的文章的“全局解释器锁”部分。
为了解决这个问题,请查看Python 的多处理模块。
与线程相比,多进程(合理使用 IPC)是编写多 CPU 机器应用程序的更好的方法。
—— Guido van Rossum(Python 创始人)
根据@spinkus 的评论进行编辑:
如果 Python 不能同时运行多个线程,那么为什么还要有线程呢?
在执行不需要修改解释器状态的同步操作时,线程在 Python 中仍然非常有用。这包括许多(大多数?)不是 Python 内部计算的长时间运行的函数调用,例如I/O(文件访问或网络请求)和 Numpy 数组上的计算。这些操作在等待结果时释放 GIL,允许程序继续执行。然后,一旦收到结果,线程必须重新获取 GIL 才能在“Python-land”中使用该结果
解决方案 2:
Python 有一个全局解释器锁,它可以防止解释代码的线程被并发处理。
http://en.wikipedia.org/wiki/Global_Interpreter_Lock
http://wiki.python.org/moin/GlobalInterpreterLock
要解决此问题,请尝试使用多处理模块,如下所示:
运行单独的 python 进程是否可以避免 GIL?
解决方案 3:
据我所知,在 CPython 中,全局解释器锁意味着一次不能运行多个 Python 代码块。虽然这实际上不会影响单处理器/单核机器上的任何内容,但在多核机器上,这意味着实际上一次只能运行一个线程 - 导致所有其他核心处于空闲状态。
扫码咨询,免费领取项目管理大礼包!