在技术交流中,很多人会混用 阻塞 和 休眠 这两个概念,主要是因为它们在某些场景下表现出的外在效果类似:线程都处于一种“无法继续执行”的状态。然而,严格来说,它们有着明显的区别,从机制到实现都有不同的内涵。
阻塞与休眠的混用现象
-
语言习惯:
- 人们常用“阻塞”泛指线程或进程暂时无法继续执行的状态,而不细究其具体机制是否是主动让出 CPU(休眠)还是忙等(真正的阻塞)。
- 比如说“线程阻塞在
read
调用上”,虽然实际实现中它可能是通过睡眠机制完成的,但大家往往并不严格区分。
-
广义阻塞:
- 有时,“阻塞”被用作一种宽泛的描述,泛指线程或进程因等待资源而停顿的状态,而未强调具体是主动休眠还是其他方式。
- 从结果上看,无论是休眠还是忙等,线程都暂时不执行,因而容易让人混淆。
-
文档和教程中的表述:
- 在一些教程或文档中,阻塞和休眠常被交替使用。例如,“
wait_event_interruptible
是阻塞调用”这样的说法其实从严格意义上不准确,应该说它是“让线程进入休眠”。
- 在一些教程或文档中,阻塞和休眠常被交替使用。例如,“
阻塞与休眠的严格区分
属性 | 阻塞 | 休眠 |
---|---|---|
定义 | 线程或进程无法继续运行,但可能仍然占用 CPU(如忙等)。 | 线程或进程主动放弃 CPU 使用权,直到条件满足时被唤醒。 |
CPU 资源使用 | 通常消耗 CPU 资源(如忙等中的循环检测)。 | 不消耗 CPU 资源,线程进入内核的等待队列,只有被唤醒时才恢复执行。 |
线程状态 | 可能仍然是 TASK_RUNNING(如果是忙等)。 | 通常是 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUPTIBLE。 |
典型场景 | 自旋锁、忙等等待资源(如死循环检测某个条件)。 | wait_event 系列函数、poll 、select 、epoll 、msleep 等异步等待机制。 |
为什么容易混淆?
-
从用户视角:
- 用户代码中,只要某个操作调用(如
read
、poll
、mutex_lock
)导致线程暂停执行,很多人就会说“线程阻塞了”。 - 这时,用户关心的是“线程没法继续运行”的状态,而非其背后的实现细节。
- 用户代码中,只要某个操作调用(如
-
机制表现相似:
- 无论线程进入阻塞还是休眠状态,都会导致线程暂停工作,等条件满足后再恢复执行,这种“暂停运行后再继续”的表象使得两者容易被混淆。
-
一些文档未作严格区分:
- Linux 内核文档和一些教材中,可能为了通俗易懂,直接把“等待某个条件”的状态统一称为“阻塞”,而未严格讨论具体是通过休眠实现的。
总结与建议
虽然阻塞和休眠的混用在技术交流中较为常见,但在涉及内核开发或性能优化时,了解它们的区别非常重要。
- 休眠 是主动让出 CPU 使用权的高效等待机制,避免浪费资源。
- 阻塞 在严格意义上更强调“当前线程无法继续运行”,可能涉及忙等或其他资源占用。
建议在深入讨论或优化时尽量明确概念,避免因混用而导致误解。例如,提到 poll
或 wait_event
时,明确它们本质上是“线程的休眠”,而不是简单的阻塞等待。
我总结出的典型的属于休眠的场景
只要是线程加入等待队列的,都属于“休眠”。
wait_event
系列函数、poll
、select
、epoll
、msleep
等异步等待机制等都属于“休眠”