阻塞式IO与非阻塞IO的区别
1. 阻塞式IO (Blocking I/O)
-
定义
当程序发起一个I/O操作(如读取文件、网络数据)时,进程会被挂起(阻塞),直到操作完成或超时才会继续执行后续代码。在此期间,程序无法执行其他任务。 -
工作流程
python
复制
data = socket.recv() # 调用recv()后,程序卡在这里等待数据 print("收到数据:", data) # 数据到达后才会执行
-
特点
-
优点:编程简单,代码直观。
-
缺点:资源利用率低(等待期间CPU空闲),不适合高并发场景。
-
-
典型场景
-
简单的单线程脚本
-
对实时性要求不高的低频操作(如命令行工具)
-
2. 非阻塞式IO (Non-blocking I/O)
-
定义
程序发起I/O操作后立即返回,无需等待结果。可以通过轮询或事件通知机制(如select
/epoll
)检查操作状态,实现并发处理多个I/O。 -
工作流程
python
复制
socket.setblocking(False) # 设置为非阻塞模式 try: data = socket.recv() # 立即返回,若有数据则返回数据,否则抛异常 print("收到数据:", data) except BlockingIOError: print("暂时无数据,继续处理其他任务") # 程序继续执行其他逻辑
-
特点
-
优点:提高CPU利用率,支持高并发(如同时处理数千连接)。
-
缺点:编程复杂,需配合多路复用或回调机制。
-
-
典型场景
-
Web服务器(Nginx、Node.js)
-
实时通信系统(WebSocket、游戏服务器)
-
3. 核心区别
维度 | 阻塞式IO | 非阻塞式IO |
---|---|---|
行为 | 调用后卡住,直到I/O完成 | 调用后立即返回,需主动查询状态 |
CPU利用率 | 低(等待期间闲置) | 高(可并行处理其他任务) |
并发能力 | 弱(依赖多线程/进程) | 强(单线程即可处理大量I/O) |
实现复杂度 | 简单 | 复杂(需搭配多路复用或异步框架) |
适用场景 | 低频、简单任务 | 高频、高并发场景 |
4. 技术实现
阻塞式IO示例(C语言)
c
复制
int fd = open("file.txt", O_RDONLY); char buf[1024]; read(fd, buf, sizeof(buf)); // 阻塞在此,直到数据读取完毕 printf("Data: %s\n", buf);
非阻塞式IO示例(C语言)
c
复制
int fd = open("file.txt", O_RDONLY | O_NONBLOCK); // 设置非阻塞标志 char buf[1024]; while (1) { ssize_t n = read(fd, buf, sizeof(buf)); if (n > 0) { printf("Data: %s\n", buf); break; } else if (n == -1 && errno == EAGAIN) { // 数据未就绪,处理其他任务 usleep(1000); } }
5. 如何选择?
-
选阻塞式IO:
-
开发简单快速的小工具。
-
无需高并发(如本地配置文件读取)。
-
-
选非阻塞IO:
-
高并发服务器(如Web服务、实时聊天)。
-
需要最大限度利用CPU资源的场景。
-
6. 扩展:同步 vs 异步IO
-
同步IO:程序主动等待I/O结果(阻塞式、非阻塞式均属此类)。
-
异步IO:程序发起I/O后无需等待,操作系统完成后主动通知(如Linux的
io_uring
、Windows的IOCP
)。