为了账号安全,请及时绑定邮箱和手机立即绑定

GIL 在 Python 3.13 中成为可选:多线程领域的游戏规则改变者!

在编程的世界中,并发是提高应用程序性能的关键因素。在 Python 中,全局解释器锁(GIL)一直是实现真正并发的主要阻碍。然而,随着即将发布的 Python 3.13,GIL 变成了可选的!这一实验性特性标志着 Python 多线程能力演进的重要一步。

在这篇文章中,我们将深入了解GIL是什么,它如何影响你的代码,以及最重要的是,如何利用Python 3.13中的这个新功能来加速你的多线程应用程序性能。

zh: GIL是什么呢?

GIL 是由 CPython 解释器用来确保任何时候只有一个线程在执行 Python 字节码的全局解释器锁。这意味着,即使你同时运行了多个线程,也只会有一个线程在执行 Python 代码。虽然这对于早期版本的 Python 来说,防止数据损坏和确保行为的一致性是必要的,但在现代应用程序中,其中并发性至关重要,这已成为现代应用程序中一个关键的瓶颈。

Python 3.13 新增了哪些功能?

Python 3.13,引入了一个实验性功能,允许你彻底关闭 GIL!这意味着线程可以更平行运行,对于某些类型的任务负载,这可能带来显著的性能提升。

为了利用这一新功能,你需要下载并安装Python 3.13的测试版(rc1,即发布候选版1),它包含了一个免费线程构建配置。然后,你可以通过环境变量或命令行参数来开启或关闭GIL。

使用可选GIL的好处

在你的 Python 应用中实现真正的并行处理可以带来深远的好处,包括:

  • 提高性能:通过允许多个线程同时运行,你就可以为计算密集型任务实现显著的速度加快。
  • 提高可扩展性:通过同时运行更多线程,你就可以处理更大的负载并更有效地扩展应用程序。

如何在Python 3.13中检测并禁用GIL[^1]

[^1]: GIL,即全局解释器锁(global interpreter lock)

具有实验性功能的 CPython 允许通过在多个核心上并行运行线程来充分利用处理能力。为线程设计的程序将在多核处理器上运行得更快。

要启用多线程构建,你需要具体来说,要么使用 --disable-gil 选项编译自己的解释器,要么安装带有自由线程标识的实验性构建。请注意,这项功能尚处于开发阶段,可能包含一些错误,并且可能对单线程性能有较大影响。

用户可以使用环境变量 PYTHON_GIL 或命令行选项 -X gil 在运行时可选地启用 GIL,这样做。

Python 代码

这段Python代码旨在衡量禁用全局解释器锁(GIL)并在并行计算中使用多线程的有效性。具体来说,它使用六个并发线程来计算相同的六个数的阶乘。

    #!/usr/bin/env python3  
    # 导入所需模块  
    import sys  
    import sysconfig  
    import math  
    import time  
    from threading import Thread  
    from multiprocessing import Process  

    # 定义一个装饰器来测量函数执行时间  
    def time_taken(func):  
        """  
        一个装饰器,用于测量函数的执行时间。  

        参数:  
            func: 目标函数。  

        返回:  
            一个包装函数,测量并打印执行时间。  
        """  
        def wrapper(*args, **kwargs):  
            start_time = time.perf_counter()  
            result = func(*args, **kwargs)  
            end_time = time.perf_counter()  
            execution_time = end_time - start_time  
            print(f"函数 {func.__name__!r} 执行了 {execution_time:.4f} 秒。")  
            return result  
        return wrapper  

    # 定义一个计算密集型任务作业函数  
    def compute_intensive_task(num):  
        """  
        计算密集型任务作业,计算数字的阶乘值。  

        参数:  
            num: 输入数字。  

        返回:  
            输入数字的阶乘值。  
        """  
        #return math.sqrt(num)  # 注释掉此行以专注于多线程  
        return math.factorial(num)  

    # 定义单线程任务函数  
    @time_taken  
    def single_threaded_task(nums):  
        """  
        单线程任务函数,顺序执行计算密集型作业。  

        参数:  
            nums: 输入数字列表。  
        """  
        for num in nums:  
            compute_intensive_task(num)  

    # 定义多线程任务函数  
    @time_taken  
    def multi_threaded_task(nums):  
        """  
        多线程任务函数,创建和运行线程池以执行任务。  

        参数:  
            nums: 输入数字列表。  
        """  
        threads = []  

        # 创建 len(nums) 个线程  
        for num in nums:  
            thread = Thread(target=compute_intensive_task, args=(num,))  
            threads.append(thread)  
            thread.start()  

        # 等待所有线程结束  
        for thread in threads:  
            thread.join()  

    # 定义多进程任务函数  
    @time_taken  
    def multi_processing_task(nums):  
        """  
        多进程任务函数,创建和运行进程池以执行任务。  

        参数:  
            nums: 输入数字列表。  
        """  
        processes = []  

        # 创建 len(nums) 个进程  
        for num in nums:  
            process = Process(target=compute_intensive_task, args=(num,))  
            processes.append(process)  
            process.start()  

        # 等待所有进程结束  
        for process in processes:  
            process.join()  

    # 定义程序的主函数  
    def main():  
        """  
        程序的主函数,打印 Python 版本并检查 GIL 是否已启用。然后运行单线程、多线程和多进程任务函数。  
        """  
        print(f"Python 版本: {sys.version}")  

        # 检查 GIL 状态  
        py_version = float(".".join(sys.version.split()[0].split(".")[0:2]))  
        status = sysconfig.get_config_var("Py_GIL_DISABLED")  

        if py_version >= 3.13:  
            status = sys._is_gil_enabled()  
        if status is None:  
            print("GIL 无法在 Python 3.12 及更早版本中禁用")  
        if status == 0:  
            print("GIL 当前已禁用")  
        if status == 1:  
            print("GIL 当前正在启用")  

        nums = [300001, 300001, 300001, 300001, 300001, 300001]  

        # 运行单线程任务函数  
        single_threaded_task(nums)  

        # 运行多线程任务函数  
        multi_threaded_task(nums)  

        # 运行多进程任务函数  
        multi_processing_task(nums)  

    # 调用主函数  
    if __name__ == "__main__":  
        main()

示例运行和分析

我们这里有三组数据:

  1. PYTHON_GIL=0 (GIL 关闭) 使用 Python 3.13-rc 版本
  2. PYTHON_GIL=1 (GIL 开启) 使用 Python 3.13-rc 版本
  3. PYTHON_GIL=1 (GIL 开启) 使用 Python 3.12 版本
    ## PYTHON_GIL=0 (禁用 GIL)使用 Python 3.13-rc  
    $ PYTHON_GIL=0 ./gil_test2.py  
    Python 版本:3.13.0rc1 实验性自由线程版本(main,2024年8月13日,11:52:30)[GCC 12.2.0]  
    当前 GIL 已禁用  
    函数 'single_threaded_task' 执行的时间为 7.5143 秒。  
    函数 'multi_threaded_task' 执行的时间为 1.3645 秒。  
    函数 'multi_processing_task' 执行的时间为 1.6192 秒。  

    ## PYTHON_GIL=1 (启用 GIL)使用 Python 3.13-rc  
    $ PYTHON_GIL=1 miteshsjat/python:3.13-rc  ./gil_test2.py  
    Python 版本:3.13.0rc1 实验性自由线程版本(main,2024年8月13日,11:52:30)[GCC 12.2.0]  
    当前 GIL 已启用  
    函数 'single_threaded_task' 执行的时间为 7.5105 秒。  
    函数 'multi_threaded_task' 执行的时间为 7.0728 秒。  
    函数 'multi_processing_task' 执行的时间为 1.4612 秒。  

    ## PYTHON_GIL=1 (启用 GIL)使用 Python 3.12;  
    ##   此处设置 PYTHON_GIL=1/0 不会产生任何效果  
    $ PYTHON_GIL=1 python:3.12-slim-bookworm  ./gil_test2.py  
    Python 版本:3.12.5(main,2024年8月13日,01:30:38)[GCC 12.2.0]  
    Python 3.12 及以下版本无法禁用 GIL  
    函数 'single_threaded_task' 执行的时间为 8.0055 秒。  
    函数 'multi_threaded_task' 执行的时间为 7.5204 秒。  
    函数 'multi_processing_task' 执行的时间为 1.5135 秒。

当我们禁用全局解释器锁(GIL)时,我们的多线程任务完成得飞快——只需 1.36秒!这比GIL处于活动状态时快了 倍。有趣的是,在Python 3.13中使用活动的GIL,结果与Python 3.12非常相似(Python 3.12不支持禁用GIL)。

zh: 结论

在这篇文章中,我们探讨了 Python 3.13 的新特性及其如何通过可选的 GIL 实现真正的并发。无论您是在构建高性能的 Web 应用程序还是计算密集型的数据处理管道,这项功能无疑将为您的多线程任务带来革命性的影响!

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消