Python进程、线程、协程、GIL

Python进程、线程、协程、GIL

基础概念

进程(Process)

进程是操作系统分配资源的基本单位,每个进程都有自己的内存空间、代码段、数据段、堆栈等。进程的特点是:

  • 独立性强:一个进程崩溃不会直接影响其他进程。
  • 系统开销大:创建和切换进程需要操作系统切换上下文和内存管理。
  • 进程间通信(IPC)复杂:常用方法有管道、消息队列、共享内存、socket等。

线程(Thread)

线程是CPU调度的基本单位,是比进程更小的执行单元。线程属于进程,多个线程共享进程的内存空间。线程的特点是:

  • 轻量级:创建、切换开销比进程小。
  • 数据共享:同一进程内线程共享全局变量和堆内存,但要注意线程安全(加锁)。
  • 受GIL限制:Python中同一时刻只能有一个线程执行Python字节码,因此CPU 密集型任务无法通过多线程实现真正并行

协程(Coroutine)

协程是一种用户态的轻量级线程,也叫微线程,特点是非抢占式调度。协程依赖事件循环,在I/O等待时主动让出控制权,节省线程切换开销,协程只在一个线程中运行,但可以同时处理大量I/O任务。协程的特点是:

  • 极轻量:成千上万协程在一个线程中也不占用大量内存。
  • 非抢占式:需要显式await或yield才会切换。
  • 适合I/O密集型任务。

全局解释器锁(GIL)

GIL是Python的一把锁,用来保证同一时刻只有一个线程执行Python 字节码。GIL存在是为了简化内存管理。GIL会导致多线程无法利用多核CPU做CPU密集型运算,I/O 密集型任务不受影响。

具体实现

  • 使用multiprocessing实现多进程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    import multiprocessing
    import os
    import time


    def process_task(task_name: str):
    print(f"Process {task_name} started,PID: {os.getpid()},Time:{time.time()}")
    time.sleep(5)
    print(f"Process {task_name} finished,time:{time.time()}")

    '''
    -------------start-----------------
    父进程
    |
    |-- p.start()
    |
    操作系统:
    ├─ 创建新进程(PID 不同)
    ├─ 分配独立内存空间
    ├─ 拷贝/初始化运行环境
    └─ 调度子进程执行
    -------------join-----------------
    父进程:WAITING
    子进程:RUNNING

    子进程退出

    父进程恢复执行
    '''

    if __name__ == "__main__":
    tasks = ["Task 1", "Task 2", "Task 3"]
    processes = []
    for task in tasks:
    p = multiprocessing.Process(target=process_task, args=(task,))
    processes.append(p)
    p.start() # 让OS调度进程,创建并启动一个并发执行单元
    for p in processes:
    p.join() # 阻塞当前进程(通常是父进程),直到子进程结束

    output:

    1
    2
    3
    4
    5
    6
    Process Task 2 started,PID: 5936,Time:1768805990.6005492
    Process Task 1 started,PID: 5935,Time:1768805990.600867
    Process Task 3 started,PID: 5937,Time:1768805990.602313
    Process Task 2 finished,time:1768805995.602974
    Process Task 1 finished,time:1768805995.605867
    Process Task 3 finished,time:1768805995.608462
  • 使用threading创建多线程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    import threading
    import os
    import time

    def thread_task(task_name: str):
    print(f"Thread {task_name} started,PID: {os.getpid()},Time:{time.time()}")
    time.sleep(5)
    print(f"Thread {task_name} finished,time:{time.time()}")

    '''
    -------------start-----------------
    进程
    ├─ 主线程
    └─ 子线程(共享内存)

    start():
    ├─ 创建 OS 线程
    ├─ 注册到当前进程
    └─ 等待 CPU 调度
    -------------join-----------------
    主线程:WAITING
    子线程:RUNNING

    子线程结束

    主线程继续
    '''

    if __name__ == "__main__":
    tasks = ["Task 1", "Task 2", "Task 3"]

    threads = []
    for task in tasks:
    t = threading.Thread(target=thread_task, args=(task,))
    threads.append(t) # 在当前进程内创建一个新的执行线程,并执行task
    t.start()
    for t in threads:
    t.join() # 阻塞当前线程,直到目标线程执行结束

    output:

    1
    2
    3
    4
    5
    6
    Thread Task 1 started,PID: 6098,Time:1768806447.717162
    Thread Task 2 started,PID: 6098,Time:1768806447.717246
    Thread Task 3 started,PID: 6098,Time:1768806447.717311
    Thread Task 1 finished,time:1768806452.721641
    Thread Task 2 finished,time:1768806452.724179
    Thread Task 3 finished,time:1768806452.724231
  • 使用asyncio实现协程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    import asyncio
    import time


    async def task(task_name: str):
    print(f"task {task_name} start,time:{time.time()}")
    await asyncio.sleep(5)
    print(f"task {task_name} end,time:{time.time()}")

    '''
    asyncio.run()
    ├─ 创建事件循环
    ├─ 创建 main Task
    ├─ 启动事件循环
    └─ main 执行
    └─ await gather
    ├─ 注册 task1/2/3
    ├─ 并发调度
    └─ 等待全部完成
    ├─ main 结束
    └─ 关闭事件循环
    '''
    async def main():
    tasks = [task(str(i)) for i in range(3)]
    await asyncio.gather(*tasks)
    '''
    协程对象:
    → create_task()
    → Task(Future)
    → 注册到事件循环
    gather:
    创建一个新的 Future
    监听所有子 Task
    等全部完成
    收集结果
    --------------时间线---------------
    A start ─┐
    B start ─┼── 并发执行
    C start ─┘

    await(让出控制权)

    事件循环调度
    '''

    asyncio.run(main())

    output:协程的输出几乎同时开始,节省了线程的切换开销,非常高效

    1
    2
    3
    4
    5
    6
    task 0 start,time:1768807095.176304
    task 1 start,time:1768807095.176335
    task 2 start,time:1768807095.176345
    task 0 end,time:1768807100.1782029
    task 1 end,time:1768807100.178287
    task 2 end,time:1768807100.178303
  • 线程安全加锁的实现

    • 同步锁Lock,不支持多次申请和多次释放
    • 递归锁RLock支持多次申请锁acquire()和多次释放锁release()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    import threading

    number = 0
    thread_lock = threading.RLock() # 递归锁


    def task_add():
    # 使用线程锁上下文申请锁
    with thread_lock:
    global number

    for i in range(1000):
    number += i
    print(f"Number is {number},Thread name is {threading.current_thread().name}")


    def task_sub():
    with thread_lock:
    global number
    for i in range(1000):
    number -= i
    print(f"Number is {number},Thread name is {threading.current_thread().name}")


    add_thread = threading.Thread(target=task_add)
    add_thread.name = "add_thread"
    sub_thread = threading.Thread(target=task_sub)
    sub_thread.name = "sub_thread"
    add_thread.start()
    sub_thread.start()

    output:

    1
    2
    Number is 499500,Thread name is add_thread
    Number is 0,Thread name is sub_thread
作者

Kim

发布于

2026-01-19

更新于

2026-01-20

许可协议

CC BY-NC-SA 4.0