[博客翻译]GIL在Python 3.13中变为可选


原文地址:https://geekpython.in/gil-become-optional-in-python


简介

在Python 3.13版本中,一个重要的变化是GIL(全局解释器锁)的可选性。GIL是CPython解释器为了确保任何时候只有一个线程执行Python字节码而设计的机制。从Python 3.13开始,这个长期存在的限制可能会被解除,允许更多的并发执行。

什么是GIL?

GIL是一个保证同一时间只有一个线程运行的核心机制,这对于单线程执行的Python代码来说是必要的,但对多线程应用来说可能是个瓶颈,因为它限制了真正的并行计算。

实验特性

Python 3.13引入了一个名为“自由线程模式”的新功能,它默认关闭GIL,让多线程能更有效地协同工作。这是一项实验性的特性,如果你想要尝试,可以从官方下载预览版安装,并在安装时选择“自由线程二进制文件(实验)”。

配置GIL

要使GIL在Python 3.13中变为可选,只需在安装时使用--disable-gil选项,这是一种构建配置,意味着选择了无GIL的构建。此外,还可以通过环境变量PYTHON_GIL来控制GIL的启用和禁用,设置为1表示启用,0表示禁用。命令行选项-X gil也有同样的作用。

Python
# v3.13
# GIL disabled
python3 -X gil=0 sample.py
 
# GIL enabled
python3 -X gil=1 sample.py

检查GIL状态

你可以通过sysconfig.get_config_var("Py_GIL_DISABLED")来检查当前Python解释器是否启用了无GIL模式。返回值为0表示GIL启用,1表示GIL已禁用。

import sys
sys._is_gil_enabled()  # returns a boolean value

GIL与无GIL性能对比

比较GIL启用和禁用时,多线程程序的性能会有显著差异。我们可以通过一个简单的Python程序(gil.py),计算数字的阶乘,来观察单线程、多线程和多进程任务的执行时间。在GIL启用时,多线程和多进程之间的性能差距较小;而在GIL禁用时,多线程性能提升明显,而单线程和多进程任务的性能可能会有所下降。

import sys
import sysconfig
import math
import time
import threading
import multiprocessing
 
def compute_factorial(n):
    return math.factorial(n)
 
# Single-threaded
def single_threaded_compute(n):
    for num in n:
        compute_factorial(num)
    print("Single-threaded: Factorial Computed.")
 
# Multi-threaded
def multi_threaded_compute(n):
    threads = []
    # Create 5 threads
    for num in n:
        thread = threading.Thread(target=compute_factorial, args=(num,))
        threads.append(thread)
        thread.start()
 
    # Wait for all threads to complete
    for thread in threads:
        thread.join()
 
    print("Multi-threaded: Factorial Computed.")
 
# Multi-process
def multi_processing_compute(n):
    processes = []
    # Create a process for each number in the list
    for num in n:
        process = multiprocessing.Process(target=compute_factorial, args=(num,))
        processes.append(process)
        process.start()
 
    # Wait for all processes to complete
    for process in processes:
        process.join()
 
    print("Multi-process: Factorial Computed.")
 
def main():
    # Checking Version
    print(f"Python version: {sys.version}")
 
    # GIL Status
    status = sysconfig.get_config_var("Py_GIL_DISABLED")
    if status is None:
        print("GIL cannot be disabled")
    if status == 0:
        print("GIL is active")
    if status == 1:
        print("GIL is disabled")
 
    numlist = [100000, 200000, 300000, 400000, 500000]
 
    # Single-threaded Execution
    start = time.time()
    single_threaded_compute(numlist)
    end = time.time() - start
    print(f"Single-threaded time taken: {end:.2f} seconds")
 
    # Multi-threaded Execution
    start = time.time()
    multi_threaded_compute(numlist)
    end = time.time() - start
    print(f"Multi-threaded time taken : {end:.2f} seconds")
 
    # Multi-process Execution
    start = time.time()
    multi_processing_compute(numlist)
    end = time.time() - start
    print(f"Multi-process time taken  : {end:.2f} seconds")
 
 
if __name__ == "__main__":
    main()

打开GIL

python gil.py
Python version: 3.12.2 (tags/v3.12.2:6abddd9, Feb  6 2024, 21:26:36) [MSC v.1937 64 bit (AMD64)]
GIL cannot be disabled
Single-threaded: Factorial Computed.
Single-threaded time taken: 9.04 seconds
Multi-threaded: Factorial Computed.
Multi-threaded time taken : 8.21 seconds
Multi-process: Factorial Computed.
Multi-process time taken  : 5.64 seconds

禁用GIL

D:/SACHIN/Python13/python3.13t gil.py
Python version: 3.13.0b3 experimental free-threading build (tags/v3.13.0b3:7b41395, Jun 27 2024, 16:17:17) [MSC v.1940 64 bit (AMD64)]
GIL is disabled
Single-threaded: Factorial Computed.
Single-threaded time taken: 9.28 seconds
Multi-threaded: Factorial Computed.
Multi-threaded time taken : 4.86 seconds
Multi-process: Factorial Computed.
Multi-process time taken  : 6.14 seconds