知识卡片
多路复用I/O为何属于同步而非异步
内容
一个非常常见的认知误区,是把select/poll这类多路复用I/O当成异步操作——这个误区本身也曾出现在本节作者早年的文章里。误区的具体表现是这样类比的:去银行办业务需要排队时,通常会先取一个号码纸条,然后等银行叫号;把”银行叫号”理解成select操作,把手里的号码纸条比作向select注册的回调函数,一旦满足可操作的条件,就据此通知办理人去处理——按这个类比,多路复用操作看起来像是异步行为。但这个理解的根本错误在于:[[同步与异步的区别]]的关键从来不是”有没有用到回调机制”,而是”最终完成这件事的动作是不是要求办理者本人亲自去做”——所有需要本人亲自完成的操作都属于同步操作,不管中间是不是注册了一个回调(比如叫号小纸条)来等待通知,还是干脆自己从头到尾一直阻塞等着,两者在”同步/异步”这个维度上是一回事。回到银行例子:需要办业务的人可以拿着叫号小纸条边等边做别的事(比如刷手机),但一旦真正叫到号、可以办理业务了,办理者本人依然需要亲自去完成这次办理——这里必须区分两种不同的状态:一是”这件事现在是不是可以做了”(条件满足的通知,比如告诉你某个文件描述符可读了),二是”这件事到底有没有真正做完”(比如真正调用read/write完成了I/O操作本身)。多路复用I/O做的事情,只是记录下来”有哪些人在等待通知”、在条件满足时负责通知办理者,但真正把这件事做完,依然要靠办理者自己去完成——只要是自己去完成的操作,就都属于同步操作。这条判断也和POSIX标准的权威定义完全吻合:同步I/O操作会导致发起请求的进程被阻塞直到操作完成;异步I/O操作不会导致发起请求的进程被阻塞——按这个定义,阻塞I/O、非阻塞I/O、I/O多路复用、信号驱动I/O这四种模型全部属于同步(因为真正的I/O操作recvfrom本身都会阻塞进程),只有真正的异步I/O模型才符合异步的定义。