Python GIL(全局解释器锁)的使用小结

 更新时间:2025年11月24日 09:21:02   作者:唐古乌梁海  
本文主要介绍了Python GIL(全局解释器锁)的使用小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

我们通常所说的GIL,指的是全局解释器锁(Global Interpreter Lock),是计算机程序设计语言解释器用于同步线程的一种机制,它使得任何时刻仅有一个线程在执行。即使在多核处理器上,使用 GIL 的解释器也只允许同一时间执行一个线程。

在Python中,GIL的存在主要是为了简化CPython解释器的实现,因为CPython的内存管理不是线程安全的。GIL可以防止并发访问Python对象,从而避免多个线程同时修改同一个对象导致的数据不一致问题。

但是,GIL也导致了一个问题:在多核CPU上,使用多线程的Python程序并不能真正地并行执行,而是通过交替执行来模拟并发。因此,对于CPU密集型的任务,使用多线程并不能提高性能,甚至可能因为线程切换的开销而降低性能。

然而,对于I/O密集型的任务(如网络请求、文件读写等),由于线程在等待I/O时会被阻塞,此时GIL会被释放,从而允许其他线程运行,因此多线程在I/O密集型任务中仍然可以提升性能。

为了克服GIL的限制,可以采用多进程(使用multiprocessing模块)来利用多核CPU,因为每个进程有自己独立的Python解释器和内存空间,因此每个进程都有自己的GIL,从而可以实现真正的并行。

什么是 GIL?

GIL(Global Interpreter Lock) 是 CPython 解释器中的一个互斥锁,它确保在任何时刻只有一个线程在执行 Python 字节码。这意味着即使在多核 CPU 上,CPython 也无法实现真正的并行线程执行。

GIL 的工作原理

+-----------------------------------------------+
|              Python 进程 (单个进程)            |
|                                               |
|  +-----------------------------------------+  |
|  |          全局解释器锁 (GIL)              |  |
|  |                                         |  |
|  |      🔒 一把锁,控制 Python 字节码执行    |  |
|  +-----------------------------------------+  |
|                    ↑                          |
|                    | (获取/释放)              |
|                    |                          |
|  +------------+  +------------+  +------------+|
|  |  线程 1    |  |  线程 2    |  |  线程 3    ||
|  |            |  |            |  |            ||
|  | Python代码 |  | Python代码 |  | Python代码 ||
|  |  执行中    |  |  等待中    |  |  等待中    ||
|  +------------+  +------------+  +------------+|
|                                               |
+-----------------------------------------------+

关键点:

  • 🔒 GIL 是进程级别的锁,不是线程级别的
  • 📍 同一时间只有一个线程能持有 GIL 并执行 Python 字节码
  • ⏳ 其他线程必须等待 GIL 被释放
import threading
import time

def count_down(n):
    while n > 0:
        n -= 1

# 单线程执行
start = time.time()
count_down(100000000)
single_time = time.time() - start

# 多线程执行
start = time.time()
t1 = threading.Thread(target=count_down, args=(50000000,))
t2 = threading.Thread(target=count_down, args=(50000000,))
t1.start()
t2.start()
t1.join()
t2.join()
multi_time = time.time() - start

print(f"单线程执行时间: {single_time:.2f}秒")
print(f"双线程执行时间: {multi_time:.2f}秒")
# 你会发现多线程可能比单线程更慢!

为什么需要 GIL?

1. 简化内存管理

Python 使用引用计数进行内存管理:

import sys

a = []
print(sys.getrefcount(a))  # 查看对象的引用计数

b = a
print(sys.getrefcount(a))  # 引用计数增加

没有 GIL 时,多个线程同时修改引用计数会导致竞争条件:

# 伪代码演示竞争条件
# 线程1: obj.ref_count += 1
# 线程2: obj.ref_count -= 1
# 如果没有同步机制,ref_count 可能出错

2. 保护内部数据结构

Python 的很多内部数据结构(如 list、dict)不是线程安全的。

GIL 的影响

CPU 密集型任务

import threading
import time

def cpu_intensive_task():
    result = 0
    for i in range(10**7):
        result += i * i
    return result

# 测试多线程性能
def test_multithreading():
    threads = []
    start_time = time.time()
    
    for _ in range(4):
        t = threading.Thread(target=cpu_intensive_task)
        threads.append(t)
        t.start()
    
    for t in threads:
        t.join()
    
    print(f"多线程执行时间: {time.time() - start_time:.2f}秒")

# 对比多进程
import multiprocessing

def test_multiprocessing():
    processes = []
    start_time = time.time()
    
    for _ in range(4):
        p = multiprocessing.Process(target=cpu_intensive_task)
        processes.append(p)
        p.start()
    
    for p in processes:
        p.join()
    
    print(f"多进程执行时间: {time.time() - start_time:.2f}秒")

# 运行测试
if __name__ == "__main__":
    test_multithreading()  # 可能比单线程还慢
    test_multiprocessing()  # 真正的并行,速度更快

I/O 密集型任务

import threading
import time
import requests

def download_site(url, session):
    with session.get(url) as response:
        print(f"Read {len(response.content)} from {url}")

def download_all_sites(sites):
    with requests.Session() as session:
        # 单线程
        start_time = time.time()
        for url in sites:
            download_site(url, session)
        print(f"单线程下载时间: {time.time() - start_time:.2f}秒")
        
        # 多线程
        start_time = time.time()
        threads = []
        for url in sites:
            thread = threading.Thread(target=download_site, args=(url, session))
            thread.start()
            threads.append(thread)
        
        for thread in threads:
            thread.join()
        print(f"多线程下载时间: {time.time() - start_time:.2f}秒")

# I/O 密集型任务中,多线程有明显优势

如何绕过 GIL 的限制

1. 使用多进程

from multiprocessing import Pool, cpu_count
import math

def is_prime(n):
    if n < 2:
        return False
    for i in range(2, int(math.sqrt(n)) + 1):
        if n % i == 0:
            return False
    return True

def find_primes_parallel(numbers):
    with Pool(processes=cpu_count()) as pool:
        results = pool.map(is_prime, numbers)
    return results

numbers = range(1000000, 1010000)
primes = find_primes_parallel(numbers)
+------------------------+
|   主进程 (协调者)       |
+------------------------+
          |
    +-----+-----+
    |           |
    v           v
+-------+   +-------+
| 进程1 |   | 进程2 |
|       |   |       |
|  🔒GIL |   |  🔒GIL |  ← 每个进程有独立的GIL
+-------+   +-------+

2. 使用 C 扩展

// primes.c
#include <Python.h>

static PyObject* find_primes_c(PyObject* self, PyObject* args) {
    Py_BEGIN_ALLOW_THREADS  // 释放 GIL
    // 执行计算密集型任务
    Py_END_ALLOW_THREADS    // 重新获取 GIL
    return Py_BuildValue("i", result);
}
+-----------------------+
|   Python 线程         |
+-----------------------+
          |
          v
+-----------------------+
|   释放 GIL 的 C 扩展   | ← 在C代码中手动释放GIL
+-----------------------+
          |
          v
+-----------------------+
|   并行计算 (无GIL限制)  |
+-----------------------+

3. 使用其他 Python 实现

  • Jython: 基于 JVM,没有 GIL
  • IronPython: 基于 .NET,没有 GIL
  • PyPy: 有 GIL,但性能更好
+----------------+----------------+----------------+
|   CPython      |   Jython       |   IronPython   |
|   (有GIL)       |   (无GIL)      |   (无GIL)      |
+----------------+----------------+----------------+

4. 使用异步编程

import asyncio
import aiohttp

async def download_site_async(session, url):
    async with session.get(url) as response:
        content = await response.read()
        print(f"Read {len(content)} from {url}")

async def download_all_sites_async(sites):
    async with aiohttp.ClientSession() as session:
        tasks = []
        for url in sites:
            task = asyncio.create_task(download_site_async(session, url))
            tasks.append(task)
        await asyncio.gather(*tasks)

# 运行异步任务
asyncio.run(download_all_sites_async(sites))

GIL 的优缺点

优点

  • 简化 CPython 实现
  • 使单线程程序更快(无锁开销)
  • 更容易集成非线程安全的 C 扩展

缺点

  • 限制多核 CPU 的利用率
  • 对 CPU 密集型多线程程序不友好
  • 可能造成性能误解

实际开发建议

# 根据任务类型选择方案:

def choose_concurrency_method(task_type, data):
    if task_type == "cpu_intensive":
        # 使用多进程
        with multiprocessing.Pool() as pool:
            return pool.map(process_data, data)
    
    elif task_type == "io_intensive":
        # 使用多线程或异步
        with ThreadPoolExecutor() as executor:
            return list(executor.map(process_data, data))
    
    elif task_type == "mixed":
        # 混合方案:进程池 + 线程池
        pass

未来展望

Python 社区正在探索移除 GIL 的方案:

  • nogil 分支:尝试移除 GIL 的实验性版本
  • subinterpreters:通过多个解释器实例实现真正的并行

总结

GIL 是 CPython 的历史遗留问题,它:

  • 主要影响 CPU 密集型多线程程序
  • 对 I/O 密集型任务影响较小
  • 可以通过多进程、C 扩展、异步编程等方式绕过

到此这篇关于Python GIL(全局解释器锁)的使用小结的文章就介绍到这了,更多相关Python GIL全局解释器锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python 语法错误:"SyntaxError: invalid character in identifier"原因及解决方法

    Python 语法错误:"SyntaxError: invalid charac

    本文给大家分享Python 语法错误:“SyntaxError: invalid character in identifier“,原因及解决方法,文末给大家补充介绍了Python出现SyntaxError: invalid syntax的原因总结,感兴趣的朋友跟随小编一起学习吧
    2023-02-02
  • Python reversed函数及使用方法解析

    Python reversed函数及使用方法解析

    这篇文章主要介绍了Python reversed函数及使用方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • 利用python打印出菱形、三角形以及矩形的方法实例

    利用python打印出菱形、三角形以及矩形的方法实例

    最近在开发中遇到一个问题,需要利用python实现菱形、三角形以及矩形等形状,发现网上这方面的资料较少,所以总结分享下,这篇文章主要给大家介绍了关于利用python打印出菱形、三角形以及矩形的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-08-08
  • Python检查和同步本地时间(北京时间)的实现方法

    Python检查和同步本地时间(北京时间)的实现方法

    这篇文章主要介绍了Python检查和同步本地时间(北京时间)的实现方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12
  • python安装教程

    python安装教程

    这篇文章主要为大家详细介绍了python安装教程,文中安装步骤介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-02-02
  • 基于Python制作一个汇率换算程序

    基于Python制作一个汇率换算程序

    这篇文章主要为大家详细介绍了如何利用Python语言制作一个汇率换算程序,文中的示例代码讲解详细,对我们学习Python有一定帮助,需要的可以参考一下
    2022-09-09
  • Pytho的HTTP交互httpx包模块使用详解

    Pytho的HTTP交互httpx包模块使用详解

    Python 的 httpx 包是一个用于 HTTP 交互的一个优秀且灵活的模块。本文进行详细的讲解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-03-03
  • 基于FME使用Python过程图解

    基于FME使用Python过程图解

    这篇文章主要介绍了基于FME使用Python过程图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05
  • JWT 登录鉴权全流程完整步骤

    JWT 登录鉴权全流程完整步骤

    JWT是一种用于在各方之间安全传输信息的开放标准,JWT的优势在于无状态、分布式支持和简单性,本文给大家介绍JWT登录鉴权全流程,感兴趣的朋友跟随小编一起看看吧
    2026-01-01
  • tensorflow 获取变量&打印权值的实例讲解

    tensorflow 获取变量&打印权值的实例讲解

    今天小编就为大家分享一篇tensorflow 获取变量&打印权值的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-06-06

最新评论