详细解释为什么说 Python “不是真的多线程”

2024年9月26日服务端开发8,164字数 1471阅读4分54秒阅读模式

详细解释为什么说 Python “不是真的多线程”

Python 被认为“不是真的多线程”主要是由于全局解释器锁 (GIL) 的存在。

GIL 是 CPython 解释器(Python 最常用的实现)中的一个机制,它限制了同时只能有一个线程执行 Python 字节码。

以下是更详细的解释:

什么是 GIL

全局解释器锁 (GIL) 是一个互斥锁,用于保护访问 Python 对象的全局状态,确保只有一个线程能够执行 Python 字节码。GIL 的存在是为了简化内存管理,使 CPython 的实现变得更简单和高效。

为什么 Python “不是真的多线程”

Python 中的全局解释器锁(GIL)就像是一扇门,只有一把钥匙,只有一个线程能在同一时间进来执行代码。即使你的程序有多个线程,但由于 GIL 的存在,实际上只有一个线程在执行 Python 代码,其他线程必须等待。

这意味着,Python 的多线程在执行 CPU 密集型任务时不会真正提高效率,因为多个线程不能同时“动手”。所以,虽然你可以用多个线程来处理任务,但在实际运算时,它们并不会同时工作。

不过,对于 I/O 密集型任务(例如网络请求或文件读写),多线程可以提升性能,因为当一个线程在等待 I/O 操作时,其他线程可以继续执行。因此,GIL 的影响主要体现在 CPU 密集型任务上,而在处理 I/O 相关的任务时,多线程还是能带来好处的。

GIL 的影响

  1. 限制 CPU 密集型任务的多线程并行执行

    • 在 CPU 密集型任务中,即使你使用多线程来分割任务,GIL 也会导致只有一个线程能在任意时刻执行 Python 字节码。这样多线程的 CPU 密集型任务并不能真正实现并行计算。
  2. I/O 密集型任务中的多线程仍有用

    • 对于 I/O 密集型任务(如网络请求、文件读写),线程在等待 I/O 操作完成时会释放 GIL,从而允许其他线程执行。因此,Python 的多线程在 I/O 密集型任务中仍然能够提高性能。

多线程的替代方案

  1. 多进程

    • 使用 multiprocessing 模块,Python 可以创建多个进程,每个进程都有自己的 Python 解释器和 GIL,因此可以实现真正的并行计算。
    from multiprocessing import Process
    
    def worker(num):
        print(f'Worker: {num}')
    
    if __name__ == "__main__":
        processes = []
        for i in range(5):
            p = Process(target=worker, args=(i,))
            processes.append(p)
            p.start()
    
        for p in processes:
            p.join()
    
  2. 异步编程

    • 使用 asyncio 模块进行异步编程,可以有效地处理 I/O 密集型任务,通过协程实现并发,而不是并行。
    import asyncio
    
    async def worker(num):
        print(f'Worker: {num}')
        await asyncio.sleep(1)
    
    async def main():
        tasks = [asyncio.create_task(worker(i)) for i in range(5)]
        await asyncio.gather(*tasks)
    
    asyncio.run(main())
    
  3. 其他解释器

    • 一些 Python 解释器如 Jython 和 IronPython 没有 GIL,可以实现真正的多线程并行。但是这些解释器的生态系统和性能可能与 CPython 有所不同。

结论

尽管 Python 的 GIL 限制了其在 CPU 密集型任务中的多线程并行能力,但在 I/O 密集型任务中,多线程仍然有用。此外,通过使用多进程或异步编程,可以绕过 GIL 的限制,实现并发或并行计算。

记一次 Python 应用开发频繁假死的问题 服务端开发

记一次 Python 应用开发频繁假死的问题

问题背景 最近在开发一款自动化的应用,其中有一个自动化任务会由下面这三个按钮控制: 逻辑也很简单,我大概画下图就是这样的: 但是,在测试时,却发现了问题: 当我点击暂停任务后,此时子线程被阻塞。如果我...
记一次在 Python 中因为文件路径导致的错误 服务端开发

记一次在 Python 中因为文件路径导致的错误

最近在编写一个自动化应用,需要管理浏览器的状态。 通过单例模式的设计,实现了只有一个浏览器实例,这样其它模块或者函数调用这个浏览器类,用的都是同一个实例,就可以管理这个浏览器的状态了。 类似下面这样调...
Python 中单例模式的实现与使用 服务端开发

Python 中单例模式的实现与使用

实现方法 在Python中,单例模式可以通过多种方法实现。单例模式的目标是确保一个类只有一个实例,并提供一个全局访问点。以下是几种常见的实现单例模式的方法: 方法 1: 使用模块 Python中的模块...