非阻塞 I/O (Non-blocking I/O) 是对阻塞 I/O 的改进。它允许进程在数据未就绪时立即返回,而不是被挂起。
核心逻辑
通过设置 文件描述符 (FD) 为非阻塞模式,I/O 调用(如 read/recv)的行为发生变化:
- 立即返回:若内核数据未就绪,立即返回错误码(如
EAGAIN),线程不挂起。 - 忙轮询 (Polling):用户进程需要通过循环不断询问内核数据是否准备好。
- 阶段性阻塞:虽然第一阶段(等待数据)不阻塞,但第二阶段(数据从内核拷贝到用户空间)依然是阻塞的。
sequenceDiagram participant U as 用户进程 participant K as 内核 (Kernel) loop 轮询 (Polling) U->>K: recvfrom (O_NONBLOCK) K-->>U: EWOULDBLOCK (数据未就绪) Note over U: 忙碌或执行其他逻辑 end U->>K: recvfrom Note over K: 阶段 2: 拷贝数据 K->>U: 返回数据
// 在 Go 中,net.Conn 封装了非阻塞细节
// 但底层通过 SetDeadline 或类似的 syscall 实现非阻塞行为
conn.SetReadDeadline(time.Now().Add(1 * time.Millisecond))
n, err := conn.Read(buf)
if err != nil {
// 如果是超时/未就绪错误,可以先处理别的
}优劣对比
- 优点:线程利用率提高,在等待数据时可以执行其他任务。
- 缺点:CPU 占用率高。由于需要不断轮询,会产生大量的系统调用和上下文切换,导致 CPU “空转”。
类比:去餐厅点餐,点完后回座位玩手机,但每隔一分钟就跑去柜台问:“菜好了吗?”。虽然没死等,但跑来跑去(轮询)非常累。