为什么我觉得同步和阻塞是一回事情?不就是因为同步才阻塞的吗
这是一个非常经典的困惑,因为在绝大多数初学者接触到的场景(比如标准的 read()/write() 调用)中,同步和阻塞确实是同时发生的。
但从底层设计和系统编程的角度来看,它们描述的是两个不同维度的问题。
核心区别:维度不同
-
同步 vs 异步 (Synchronous vs Asynchronous):
- 关注点:消息通信机制,即 “结果是谁返回给我的?”
- 同步:调用者主动等待结果。不拿到结果,调用就不算完。
- 异步:调用发出后直接返回。等结果好了,由“被调用者”(操作系统)通过回调或通知来告诉调用者。
-
阻塞 vs 非阻塞 (Blocking vs Non-blocking):
- 关注点:调用者在等待时的状态,即 “等的时候我能不能干别的?”
- 阻塞:在结果出来前,当前线程被挂起(CPU 不再分配时间片),什么都干不了。
- 非阻塞:即使结果没出来,调用也立刻返回一个状态(比如
EAGAIN),线程不挂起,可以继续跑。
为什么你会觉得它们是一回事?
因为最常见的 I/O 模型是 同步阻塞 (Blocking I/O)。
- 例子:同步阻塞
你打外卖电话下单,老板说“别挂电话,我这就去做”,然后你一直拿着手机在等,手机也干不了别的。
- 同步:你必须在那等着拿结果。
- 阻塞:你拿着手机的手(线程)被占用了,啥也干不了。
关键证据:同步但不阻塞 (Synchronous Non-blocking)
如果同步和阻塞是一回事,那么“同步非阻塞”就不可能存在。但实际上它是存在的,最典型的就是 轮询 (Polling)。
- 例子:同步非阻塞 (轮询)
你打外卖电话:“饭好了吗?”
老板:“还没。”(立刻返回结果,不挂断你电话,也不让你死等)
你挂了电话,玩了会手机(线程没挂起),过两分钟再打:“好了吗?”
老板:“还没。”
… 循环往复。
- 同步:因为你仍然需要主动一遍遍去问结果,而不是坐等老板敲你门(异步)。
- 非阻塞:因为老板没让你在电话里死等,每次询问都会立刻给你个答复,你的线程可以去做别的事。
总结对照表
| 组合 | 你的状态 | 行为描述 |
|---|---|---|
| 同步阻塞 | 死等 | 你在柜台等奶茶,不拿到奶茶不离开,期间啥也不干。 |
| 同步非阻塞 | 轮询 | 你在柜台问了一句,店员说没好,你转头去刷了会朋友圈,过一会又来问。 |
| 异步非阻塞 | 通知 | 你扫码下单,然后回座位看书。奶茶好了,小程序弹窗通知你,你再去取。 |
回答你的问题:
“不就是因为同步才阻塞的吗?” —— 不是。
阻塞是因为内核把你的线程状态改成了 TASK_INTERRUPTIBLE(挂起)。你可以选择“同步且不阻塞”(轮询),这样虽然你还是得主动拿结果(同步),但你的线程并没有停下来。
一个是逻辑上的等待(必须拿到结果才能走下一步),一个是物理上的挂起(CPU 停掉你的任务)。