Python 3.13自由线程实测:这次多线程终于能同时跑满CPU核心了

2026-09-07 0 146

过去提到Python的多线程,大家第一反应就是“有GIL锁,CPU密集型任务没戏”。所以我一直老老实实用multiprocessing绕开这个坎,直到前阵子看到Python 3.13的发布说明里多了一个自由线程模式(free-threading),官方叫“3.13t”,也就是不带GIL的构建版本。

看完我就坐不住了,周末特意腾出时间折腾了一下。毕竟,如果这玩意儿真有那么神奇,以后写并行代码可能就不需要再序列化数据、用ProcessPoolExecutor那一套了。

这个“自由线程”到底是什么

简单说,Python 3.13开始提供了一种特殊构建,叫做“free-threaded build”,在这个版本里,解释器禁用了全局解释器锁。也就是多个线程可以真正同时在多个CPU核心上执行Python字节码。

注意,它不是运行时给你一个开关,而是相当于编译了一个没有GIL的Python解释器。你需要单独安装二进制或者自己编译。在官方下载页面或者通过python.org的安装包可以选择“3.13t”版本。目前Windows、macOS和Linux都有对应的安装包。

我是在Linux上测试的,直接下载了python3.13t的二进制,解压以后是一个独立的环境,不会干扰系统里原来的Python。

wget https://www.python.org/ftp/python/3.13.0/Python-3.13.0.tar.xz
tar -xf Python-3.13.0.tar.xz
cd Python-3.13.0
./configure --disable-gil
make -j$(nproc)
sudo make install

编译过程大概花了几分钟。安装好之后,你的系统里会多一个python3.13t可执行文件。运行一下看看版本:

python3.13t -V
# Python 3.13.0 experimental free-threading build

看到“experimental free-threading build”就说明成功了。

写段代码验证奇迹还是幻觉

我写了一个比较常见的计算密集任务——计算从1到某个数的平方和。为了方便测试多线程并行,我把它拆成四个子任务,分别交给线程池处理。

import threading
import time
from concurrent.futures import ThreadPoolExecutor

def heavy_calc(start, end):
    total = 0
    for i in range(start, end):
        # 故意做一些无意义的运算
        total += i * i % 1000000
    return total

def run_multi_thread():
    ranges = [
        (1, 2500000),
        (2500000, 5000000),
        (5000000, 7500000),
        (7500000, 10000000),
    ]
    start = time.perf_counter()
    with ThreadPoolExecutor(max_workers=4) as pool:
        futures = [pool.submit(heavy_calc, r[0], r[1]) for r in ranges]
        for f in futures:
            f.result()
    return time.perf_counter() - start

if __name__ == "__main__":
    print("耗时:", run_multi_thread())

如果是普通版的Python 3.12,跑这个代码我预计会非常慢,因为四个线程抢一把锁,时间主要花在切换等待上。而在自由线程版解释器上,四个线程理论上可以各跑各的。

测试结果:令人意外的提升

我先用系统自带的Python 3.12(有GIL)跑了一遍,然后切到python3.13t跑同样的代码。结果如下:

# 普通 Python 3.12
耗时: 1.862 秒

# 自由线程 Python 3.13t
耗时: 0.492 秒

自由线程版本足足快了3.7倍左右,接近四核并行的理想值。这还只是把任务切成四份。如果差更多,效果会更夸张。

等一下,我知道你可能会问:会不会是因为Python 3.13本身比3.12更快?我也做了个对照实验——在同样的自由线程解释器下,把任务的max_workers改成1。结果单线程耗时是1.9秒,跟普通版的单线程差不多。所以提速不是版本优化带来的,而是因为多线程真的并行执行了。

那一刻,我怎么感觉有点想哭——十几年啊,CPython终于他妈的有种了。

但是,先别急着把multiprocessing全删了

自由线程目前还是“实验性”阶段。我之前看到官网上写过这样一句:这个模式会牺牲掉一些单线程性能,因为申请内存、引用计数保护都要额外开销。实测普通版本处理同样的纯计算需求,比3.13t单线程快一丢丢(大概5%~10%)。也许这就是去掉GIL需要付出的代价。

如果你现在的项目是纯Python代码,且没有太多无法绕过的C扩展依赖,那可以考虑尝试。但是很多常用库,比如numpy、pandas、cryptography,它们内部的C扩展是不是线程安全?这个还不敢保证。官方文档也说,需要这些库针对自由线程构建做适配,或者通过给每个线程加锁来保护内部状态。

我试着在自由线程解释器里导入numpy,直接崩了,提示只支持非自由线程版。所以实际生产项目还是得老老实实等主流库适配。

用text with real code 跑一个更实际的场景

纯数学计算有点无聊,我换了个贴近业务的东西——对一堆字符串文件做词频统计。比如同时读四个文件,每个文件大概几万行,统计每个文件的单词数,然后汇总。用线程池跑四个文件,每个线程读取并解析一个文件。

import threading
from concurrent.futures import ThreadPoolExecutor

def count_words(filename):
    count = 0
    with open(filename, 'r', encoding='utf-8') as fp:
        for line in fp:
            count += len(line.split())
    return count

def run_with_threads(files):
    with ThreadPoolExecutor(max_workers=4) as executor:
        results = list(executor.map(count_words, files))
    return sum(results)

if __name__ == '__main__':
    files = ['data1.txt', 'data2.txt', 'data3.txt', 'data4.txt']
    print(run_with_threads(files))

这个场景算是“混合”的:文件读取时会有一些系统IO,但split和计数则是纯CPU工作。在普通线程版里,因为GIL的限制,多线程甚至可能比单线程还要慢,因为每次开关锁也有开销。但在3.13t版本上,文件读取和解析能并行,跑完确实快,而且写起来也简单,不需要担心传大文件队列给子进程的麻烦。

线程还是不能无限开

自由线程不代表你可以无限制地创建线程,它只是去掉了GIL,JVM式的调度,但Python虚拟机的运行时本身仍然会做一些原子操作保护,比如分配内存的时候内部有锁。当你开启成千上万个线程做纯计算,线程切换和内存分配抢占也会成为瓶颈。

我在16核的机器上试过16个线程,效率最高。改成64个线程,速度反而变慢,因为CPU核心数只有那么多,线程上下文切换开销开始占上风。所以用了自由线程,照样需要合理地控制线程数量。

什么时候能等来“默认就无GIL

Python官方团队已经在规划Python 3.14或3.15里考虑把自由线程作为默认特性吗?实际上Python的指导委员会还在评估。自由线程构建目前是一个独立功能,需要额外编译。也许再过两三个小版本,官方会把它变为标准构建的一部分。但兼容性问题太多,哪怕是标准类型list和dict,底下也加了细粒度的锁。这些锁在某些情况下会产生死锁,所以他们必须非常谨慎。

不过说实话,作为一个日常拿Python写爬虫和并行脚本的人,能看到官方朝着这个方向迈出实质性一步,心里是非常激动的。至少意味着未来确实有希望告别ProcessPoolExecutor的酸爽(你得序列化所有参数,然后还得处理if __name__ == ‘__main__’)。

小结:该不该现在就用

我的建议很明确:

  • 如果你只是好奇或者想做实验,放心大胆下载3.13t体验一下,它不会弄坏你系统里的原版Python。
  • 如果是写生产用的服务或者小工具,暂时不要在生产环境使用自由线程版。别贪图这一时爽,插件库不兼容会让你脑壳疼。
  • 如果你维护的是纯Python计算模块,且没有太多第三方C扩展,你可以悄悄在非关键任务中用自由线程模式来加速,记得做好回归测试。

抛开GIL之后,Python的多线程编程模式也就没什么可怕的了。线程之间依然要面对竞态条件,但至少不会因为一个全局锁而白白浪费CPU。希望在不久的将来,我能用普通的python3命令直接跑满多核线程,不用再费劲搞多进程。

最后放一句我自己改的项目里看到的话:我们不需要再问“GIL什么时候走”,因为它正在悄悄退场,只是还需要一点时间。

Python 3.13自由线程实测:这次多线程终于能同时跑满CPU核心了
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

版权声明:
本站资源有的来自互联网收集整理,本站纯免费分享提供学习使用,如果侵犯了您的合法权益,请发送邮件1506151422@qq.com联系,将会及时下架删除。
本站资源仅供研究、学习交流之用,免费开源项目不代表完全可商用,若商业用途请先咨询开发企业能否商用,否则产生的一切后果将由下载用户自行承担。
原创板块未经允许不得转载,否则将追究法律责任。

淘吗网 python Python 3.13自由线程实测:这次多线程终于能同时跑满CPU核心了 https://www.taomawang.com/server/python/2724.html

下一篇:

已经没有下一篇了!

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务