当前位置:首页>python>Python 多线程明明开了两个任务,为啥先跑完 A 再跑 B?多进程却能同时执行?

Python 多线程明明开了两个任务,为啥先跑完 A 再跑 B?多进程却能同时执行?

  • 2026-10-11 05:42:45
Python 多线程明明开了两个任务,为啥先跑完 A 再跑 B?多进程却能同时执行?

很多初学 Python 并发的小伙伴都会遇到同一个诡异问题:

两段几乎一模一样的代码,分别创建两个任务并行执行:

1. 使用threading.Thread启动两个计算函数,运行结果永远先执行完 func1,再执行 func2,明显串行排队;

2. 使用multiprocessing.Process启动同样两个函数,两个方法同时运行,耗时直接减半。

明明都是同时启动,执行顺序却天差地别,不少人疑惑:难道线程不支持并发?进程天生就能并行?

今天结合可运行实测代码 + 底层原理,一次性把这个高频坑讲明白,看完彻底分清线程、进程适用场景。

一、复现问题:两段对比代码

代码 1:threading 双线程(纯计算,串行执行)

import threading

import time

def func1():

    # CPU密集运算

    total = sum(i for i in range(10**7))

    print("func1 执行完成")

def func2():

    total = sum(i for i in range(10**7))

    print("func2 执行完成")

if __name__ == "__main__":

    start = time.time()

    t1 = threading.Thread(target=func1)

    t2 = threading.Thread(target=func2)

    t1.start()

    t2.start()

    t1.join()

    t2.join()

    print(f"总耗时:{time.time() - start:.2f}s")

运行输出固定顺序:

func1 执行完成

func2 执行完成

总耗时≈两次计算累加(4 秒左右)

代码 2:multiprocessing 双进程(纯计算,并行执行)

import multiprocessing

import time

def func1():

    total = sum(i for i in range(10**7))

    print("func1 执行完成")

def func2():

    total = sum(i for i in range(10**7))

    print("func2 执行完成")

if __name__ == "__main__":

    start = time.time()

    p1 = multiprocessing.Process(target=func1)

    p2 = multiprocessing.Process(target=func2)

    p1.start()

    p2.start()

    p1.join()

    p2.join()

    print(f"总耗时:{time.time() - start:.2f}s")

运行输出随机顺序,几乎同时结束:

func1 执行完成

func2 执行完成

总耗时≈单次计算(2 秒左右)

现象总结:同样两个计算任务,线程排队串行,进程同步并行。

二、核心根源:CPython GIL 全局解释器锁

1. 什么是 GIL?

GIL 全称全局解释器锁,是 CPython 自带的一把互斥锁,硬性规定:

同一个进程内,同一时刻只能有 1 个线程执行 Python 字节码。

通俗比喻:

一个办公室(进程)里,只有 1 张通行证(GIL 锁),所有员工(线程)轮流抢通行证。拿到通行证才能干活,没拿到只能原地等待。

2. 为什么纯计算线程会固定先跑完 func1?

纯 CPU 运算代码全程持续占用 GIL,只有两种场景会释放锁:

1. 线程执行满固定时间片(几十毫秒);

2. 线程函数执行完毕。

代码执行流程拆解:

1. t1.start() 启动线程 1,优先抢到 GIL,开始疯狂循环计算;

2. t2.start() 启动线程 2,没有通行证,持续阻塞等待;

3. 海量循环计算会一次性耗尽任务,还没到系统切换时间片,func1 直接运行结束;

4. func1 结束后释放 GIL,func2 才能拿到锁,开始计算。

肉眼效果:固定先执行 func1,再执行 func2,完全串行。

补充:带 IO/sleep 的线程不会串行

如果函数里加入time.sleep()、网络请求、文件读写等 IO 操作,线程会主动释放 GIL,此时两个线程真正并发,结束顺序随机:

import threading, time

def func1():

    time.sleep(2)

    print("func1 完成")

def func2():

    time.sleep(2)

    print("func2 完成")

t1 = threading.Thread(target=func1)

t2 = threading.Thread(target=func2)

t1.start()

t2.start()

t1.join()

t2.join()

运行后两个函数几乎同时打印,不存在固定先后顺序。

👉 结论:GIL 只限制 CPU 密集运算,IO 阻塞会自动释放锁,线程可并发。

三、多进程为什么能同时执行两个方法?

进程是操作系统资源分配最小单位,和线程有本质区别:

1. 每创建一个Process,都会生成独立全新 Python 解释器、独立内存空间、独立 GIL 锁;

2. 操作系统调度器可把不同进程分配到不同 CPU 物理核心,真正并行运算;

3. 进程之间互不干扰,不存在抢同一把 GIL 锁的问题。

流程拆解:

1. p1、p2 是两个完全独立操作系统进程;

2. 双核 CPU 下,系统分别给两个进程分配核心;

3. func1、func2 同时在两个核心上计算,同步完成,耗时减半。

特殊情况:单核 CPU 跑多进程

如果电脑只有单核,进程依然会交替切换执行,但不会出现线程那种固定排队现象,只是无法实现多核加速。

最新文章

随机文章