page contents

Python并发:GIL与多线程的爱恨情仇,这个绊脚石终于被搞明白了!

用人话来说就是:Python在任意时刻,只允许一个线程执行字节码。就算你开了100个线程,同一时刻也只有1个线程在干活。

attachments-2026-05-ozXtZAnd6a18eef980bb0.png今天要聊一个老生常谈但又让无数人头疼的话题——GIL。

一提到Python并发,很多人第一反应就是:"Python有GIL,跑不快。"

这话对不对。对。但也不全对。今天混子哥就带你彻底搞懂GIL这个磨人的小妖精,到底是怎么影响你代码性能的。

先说GIL到底是啥,别整那些学术解释

GIL = Global Interpreter Lock,全局解释器锁。

用人话来说就是:Python在任意时刻,只允许一个线程执行字节码。就算你开了100个线程,同一时刻也只有1个线程在干活。

这就好比什么呢。你开了10个窗口排队买奶茶,结果店员说了:"不好意思,一次只能接待一位客户。"

你说气不气。

为什么要有GIL?这玩意儿存在的理由

等等,既然GIL这么碍事,为啥Python不把它去掉?

历史原因加实用主义:

设计早期没考虑多线程:Python诞生于1991年,那时候多核CPU还是科幻小说

线程安全太难做:Python对象都是引用计数,多线程修改同一个对象。那叫一个乱

取舍之道:单线程快锁,多线程慢但省心

# 引用计数演示
import sys

a = []
print(sys.getrefcount(a))

b = a
print(sys.getrefcount(a))

这就是为什么需要GIL——没有它,引用计数就是一场噩梦。

多线程 vs 多进程,性能差距有多大?

import time
import threading
import multiprocessing

def cpu_task(n):
    total = 0
    for i in range(n):
        total += i ** 2
    return total

N = 50_000_000
THREADS = 4

# 1. 单线程
start = time.time()
cpu_task(N)
print(f"单线程: {time.time() - start:.2f}秒")

# 2. 多线程
start = time.time()
threads = [threading.Thread(target=cpu_task, args=(N // THREADS,)) for _ in range(THREADS)]
[t.start() for t in threads]
[t.join() for t in threads]
print(f"多线程: {time.time() - start:.2f}秒")

# 3. 多进程
start = time.time()
with multiprocessing.Pool(THREADS) as pool:
    pool.map(cpu_task, [N // THREADS] * THREADS)
print(f"多进程: {time.time() - start:.2f}秒")

典型输出:

单线程: 4.23秒
多线程: 4.18秒
多进程: 1.12秒

所以——CPU密集型用多进程,I/O密集型用多线程。

既然GIL这么讨厌,有没有办法绕过?

方法一:multiprocessing——进程池走起

from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor

def heavy_task(n):
    return sum(i * i for i in range(n))

# 进程池——适合CPU密集型
with ProcessPoolExecutor(max_workers=4) as executor:
    results = list(executor.map(heavy_task, [10_000_000] * 8))

# 线程池——适合I/O密集型
with ThreadPoolExecutor(max_workers=4) as executor:
    results = list(executor.map(heavy_task, [10_000_000] * 8))

方法二:换一个没有GIL的Python

安装PyPy或Cython,可以避开GIL。或者直接用Jython、IronPython,但生态差很多。

或者——用C扩展把性能瓶颈包出去。比如NumPy、Pandas这种底层都是C写的,GIL管不到。

方法三:asyncio——单线程并发模型

import asyncio

async def io_task(name):
    print(f"{name} 开始")
    await asyncio.sleep(1)
    print(f"{name} 结束")

async def main():
    await asyncio.gather(*[io_task(f"任务{i}") for i in range(10)])

asyncio.run(main())

混子哥掏心窝子的话

GIL避坑指南:

CPU密集型代码(计算、排序、加密)→ 用multiprocessing,别用多线程

I/O密集型代码(网络请求、文件读写)→ 用asyncio或多线程

数据处理 → 直接上NumPy/Pandas,底层是C,GIL管不着

真正需要并行 → 上ProcessPoolExecutor,进程隔离才是真爱

别再问"GIL怎么去掉"这种问题了:

Guido说了,短期内不会去掉GIL

去掉GIL会导致单线程性能下降50%

历史包袱太重,改动代价太大

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

 

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

2083 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 2228 文章
  3. Pack 2083 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章