阻塞与非阻塞与多路复用的区别
先搞懂三个概念——阻塞、非阻塞、多路复用的区别
一个可能的问题是:“阻塞 IO 和非阻塞 IO 有什么区别?IO 多路复用又是什么?“——三句话 + 一张图说清。
三者的核心区别——用”等快递”一次讲透
graph TD subgraph 阻塞 IO Blocking IO A1[你打电话给快递员] --> A2[举着电话一直等] A2 --> A3[快递到了 挂电话] A4[特点:等的时候啥也不干] end subgraph 非阻塞 IO Non-blocking IO B1[你打电话问到了吗] --> B2[还没 挂了] B2 --> B3[过一会再打] B3 --> B4[还没 又挂了] B4 --> B5[再打 到了] B6[特点:反复问 不卡但累] end subgraph IO 多路复用 IO Multiplexing C1[你雇了一个前台] --> C2[快递到了前台签收] C2 --> C3[前台微信通知你] C4[特点:前台帮你盯着<br/>来了通知你 不轮询] end
代码层面——三种模式各长什么样
"""
三种 IO 模式用伪代码对比——看完就知道为什么多路复用是最优解。
"""
# 问题:一个线程只能处理一个连接
def server_blocking():
while True:
client = accept() # 卡住!等人连接
data = recv(client) # 卡住!等客户端发数据
send(client, b"ok") # 可能卡住!
# 问题:虽然不卡了,但需要不断轮询——白白浪费 CPU
def server_nonblocking():
clients = []
while True:
new_client = accept_nonblocking() # 不卡!没连接就返回 None
if new_client:
clients.append(new_client)
for c in clients: # 要挨个问!
data = recv_nonblocking(c) # 不卡!没数据就返回 None
if data:
handle(data)
# CPU 一直在跑这个 while True 循环——即使什么连接都没有
# → CPU 空转,浪费电
# epoll 帮你盯着所有连接——谁有数据了就通知你,你只处理有数据的
def server_multiplexing():
epoll = create_epoll() # 创建一个 epoll 实例(前台)
server_fd = create_server()
epoll.add(server_fd, READ) # 告诉 epoll:"帮我盯着 server,有人连就告诉我"
clients = {}
while True:
events = epoll.wait() # 🔴 卡住!但这里卡住是对的——
# 没有事件时线程睡眠,不浪费 CPU
# 有事件时立即返回——只返回就绪的 fd
for fd, event in events: # 只处理就绪的!不用挨个问!
if fd == server_fd:
client = accept() # 有新连接
epoll.add(client, READ) # 也交给 epoll 盯着
clients[client] = True
else:
data = recv(fd) # 有数据到了
if data:
handle(data)
else:
# 客户端断开连接
epoll.remove(fd)
del clients[fd]
一张图总结三者的 CPU 利用率
graph LR subgraph 阻塞 IO CPU利用率极低 A1[等] --> A2[A干活] --> A3[等] --> A4[A干活] end subgraph 非阻塞 IO CPU利用率高但浪费在问 B1[干活] --> B2[问] --> B3[干活] --> B4[问] end subgraph 多路复用 CPU利用率刚好 C1[睡觉] -->|被叫醒| C2[干活] -->|干完| C3[睡觉] -->|被叫醒| C4[干活] end
速记卡(面试闪卡)
Q1:一句话讲清「阻塞与非阻塞与多路复用的区别」到底是什么?
A:阻塞、非阻塞、IO 多路复用是三种处理 IO 等待的方式,多路复用用最少资源盯住所有连接。
Q2:阻塞 IO:举着电话一直等 —— 怎么理解?
A:像打电话死等快递:accept/recv 卡住线程,等的时候啥也不干,一个线程只能服务一个连接(Blocking IO)。
Q3:非阻塞 IO:反复问不卡但累 —— 怎么理解?
A:像每隔几秒打一次问到了没:不卡线程,但要在循环里不断轮询所有连接,CPU 空转浪费(Non-blocking IO)。
Q4:IO 多路复用:雇个前台帮你盯 —— 怎么理解?
A:像雇前台签收快递:epoll 帮你盯着所有 fd,谁有数据才通知你,没事件就睡觉不浪费 CPU(IO Multiplexing)。
Q5:三者 CPU 利用率对比 —— 怎么理解?
A:像三种排班:阻塞在等、非阻塞在问、多路复用在睡—被叫醒才干;多路复用 CPU 利用最合理,是高并发最优解(CPU Utilization)。
Q6:核心速记主线有哪些?
-
阻塞 IO:调用即卡住,一线程一连接,利用率低
-
非阻塞 IO:立即返回但需轮询,CPU 空转浪费
-
IO 多路复用:epoll 监听就绪事件,只处理有数据的 fd
-
多路复用最优:无事件时睡眠、有事件才唤醒,高并发首选
口诀
A:阻塞打电话,举着不动傻等他
非阻塞轮询,CPU 空转像傻瓜
多路复用好,前台 epoll 替你盯
睡觉被叫醒,高并发里它最行