一、线程基础概念

大家好,我是你们的老朋友。今天咱们聊聊多线程编程里最基础、也最绕不开的几个概念。说实话,我刚开始接触多线程时,也被这些概念搞得晕头转向。但别担心,咱们一步步来,把这些硬骨头啃下来。

1.1 进程与线程的区别

先问大家一个问题:你打开一个浏览器,又打开一个,这两个程序之间会不会互相干扰?

答案是不会。每个程序都运行在自己的独立空间里,这个独立空间就是进程。而一个程序内部,比如浏览器里同时打开多个标签页,每个标签页就是一个线程。

我打个比方你就明白了:

  • 进程就像一家独立的餐厅——有自己的厨房、服务员、收银台,别人家的餐厅管不着你
  • 线程就像餐厅里的多个厨师——共享同一个厨房,用同一口锅,但各炒各的菜

说白了,进程是资源分配的最小单位,线程是CPU调度的最小单位。我当年做第一个多线程项目时,就因为没搞清楚这个区别,把本该用线程的地方用了进程,结果内存直接爆了。嗯,那教训挺深刻的。

对比维度 进程 线程
资源开销 大(独立内存空间) 小(共享内存空间)
通信方式 IPC(管道、消息队列等) 直接读写共享变量
切换成本 高(需要切换页表) 低(只需切换寄存器)
健壮性 一个进程挂了不影响其他 一个线程挂了可能拖垮整个进程

核心要点:如果你需要高隔离性、高稳定性,选进程;如果你追求高性能、低开销,选线程。但记住——天下没有免费的午餐。

1.2 线程的生命周期

线程不是生下来就一直跑到底的。它有自己的生命周期,就像人一样,有出生、成长、休眠、死亡。

我个人习惯把线程的生命周期分为五个阶段:

  1. 新建(New):线程对象刚创建,还没开始跑
  2. 就绪(Runnable):调用了start(),等待CPU分配时间片
  3. 运行(Running):CPU正在执行这个线程的代码
  4. 阻塞(Blocked):因为某些原因(比如等待I/O、获取锁)暂停执行
  5. 死亡(Terminated):线程执行完毕或被强制终止

你想想看,一个线程大部分时间其实不是在运行,而是在等待。我做过一个性能分析,发现很多线程90%的时间都在阻塞状态。所以优化线程阻塞,往往比优化计算逻辑更有效。

小技巧:用Python的threading.enumerate()可以查看当前所有存活线程的状态。我在调试死锁问题时,这招帮了大忙。

1.3 Python中的GIL全局解释器锁

好,接下来这个知识点,可以说是Python多线程的「命门」了。

GIL,全称Global Interpreter Lock,全局解释器锁。说白了,就是Python解释器里的一把大锁,保证同一时刻只有一个线程在执行Python字节码。

为什么会这样?这得从Python的内存管理说起。Python使用引用计数来管理内存,如果多个线程同时修改对象的引用计数,就会导致内存泄漏甚至崩溃。所以CPython干脆加了一把大锁,简单粗暴地解决了这个问题。

但代价是什么?——多线程在CPU密集型任务上,可能比单线程还慢。

我曾经做过一个测试:用4个线程去计算斐波那契数列,结果比单线程还慢了20%。因为线程切换本身就有开销,而GIL又让它们没法真正并行。

注意:GIL只影响CPU密集型任务。对于I/O密集型任务(比如网络请求、文件读写),多线程依然能显著提升性能。因为线程在等待I/O时会释放GIL,其他线程就能趁机执行。

那怎么绕过GIL呢?我常用的方法有几种:

  • 使用多进程:每个进程有自己的GIL,真正并行
  • 使用C扩展:在C代码里释放GIL
  • 使用异步编程:asyncio单线程搞定高并发
  • 换解释器:比如Jython、IronPython没有GIL

来看个简单的代码示例,感受一下GIL的存在:

import threading
import time

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

# 单线程
start = time.time()
count_down(10000000)
print(f"单线程耗时: {time.time() - start:.2f}秒")

# 多线程
start = time.time()
t1 = threading.Thread(target=count_down, args=(5000000,))
t2 = threading.Thread(target=count_down, args=(5000000,))
t1.start()
t2.start()
t1.join()
t2.join()
print(f"多线程耗时: {time.time() - start:.2f}秒")

运行结果会让你大吃一惊——多线程版本反而更慢。这就是GIL的「功劳」。

避坑指南:我曾经在项目里用多线程做图像处理,结果发现8核CPU只跑满了1核。后来换成多进程,性能直接翻了6倍。所以记住——CPU密集用多进程,I/O密集用多线程。

好了,这一章的内容就到这里。咱们把进程与线程的区别、线程的生命周期、GIL这三个核心概念都捋了一遍。下一章我会带大家深入线程同步与互斥,那才是多线程编程真正「烧脑」的地方。

线程基础概念知识体系 进程与线程 线程生命周期 GIL全局锁 资源分配单位 CPU调度单位 独立内存空间 共享内存空间 新建 就绪 运行 阻塞 死亡 CPU密集:多进程 I/O密集:多线程 C扩展释放GIL 异步编程替代 理解这些概念,是写好并发程序的第一步

可可精品资料库