非阻塞 I/O (Non-blocking I/O) 是对阻塞 I/O 的改进。它允许进程在数据未就绪时立即返回,而不是被挂起。

核心逻辑

通过设置 文件描述符 (FD) 为非阻塞模式,I/O 调用(如 read/recv)的行为发生变化:

  1. 立即返回:若内核数据未就绪,立即返回错误码(如 EAGAIN),线程不挂起。
  2. 忙轮询 (Polling):用户进程需要通过循环不断询问内核数据是否准备好。
  3. 阶段性阻塞:虽然第一阶段(等待数据)不阻塞,但第二阶段(数据从内核拷贝到用户空间)依然是阻塞的
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 “空转”。

类比:去餐厅点餐,点完后回座位玩手机,但每隔一分钟就跑去柜台问:“菜好了吗?”。虽然没死等,但跑来跑去(轮询)非常累。