1、异步编程基础概念:同步与异步、阻塞与非阻塞

说实话,这章看起来基础,但我见过不少做SDK集成的同事,就是在这四个词上栽了跟头。你想想看,如果连同步异步都分不清,后面做回调改造时会迷糊。

我个人习惯,讲这组概念时喜欢从生活的例子切入。等弄明白了,再往代码里套。

1.1 同步 vs 异步:一个打电话,一个发微信

什么叫同步?说白了就是你给客服打电话。

  • 你问问题,对方回答之前,你只能举着手机等着。
  • 你动不了,电话不挂,你就干不了别的。
  • 这就是典型的同步模型。发起请求后,必须等结果返回,才能继续往下走。

那异步呢?更像发微信。

  • 你把消息发出去,不用盯着屏幕等回复。
  • 你可以刷朋友圈、回邮件、点外卖。
  • 等对方回你消息时,手机"叮"一声通知你。
  • 这就是异步。发起请求后不阻塞当前流程,等结果好了再通过某种方式通知你。

我在项目中遇到过一位刚转Python的后端同学,他把SDK里的异步接口当同步调,结果主线程直接卡死。后来我让他换了个思路——把每次调用想象成给服务器发了条微信,你该干嘛干嘛,等回调来了再处理。

我的经验:刚接触异步时,别老想着"底层怎么实现的"。先接受这个模型:发请求 → 不等,继续走 → 被通知。后面再深挖实现细节。

1.2 阻塞 vs 非阻塞:你在等,还是不等?

阻塞和非阻塞,强调的是调用方的状态。

阻塞:调用一个函数后,这个函数没返回,你就卡在那儿。什么事都做不了,CPU干等着。

// 典型阻塞调用
data = socket.recv(1024)  # 没数据来,这行代码就卡死在这里
print("这行代码只有在收到数据后才能执行")

非阻塞:调用后立刻返回,不论结果是否就绪。没数据?那返回一个空或者抛个"再试一次"的错误。

# 设置非阻塞模式
socket.setblocking(False)
try:
    data = socket.recv(1024)
except BlockingIOError:
    print("没数据,我先干别的去了")

我记得有一次帮同事排查SDK超时问题。他的代码里用了一个阻塞读,网络稍微一抖,整个UI就死了。后来换成非阻塞加轮询,体验好很多。

曾经踩过的坑:非阻塞不是万能的。如果你在一个循环里疯狂轮询非阻塞接口,CPU占用率会直接飙上去。这就像你每秒钟解锁手机800次看有没有新消息——电池受不了,别人的体验也受影响。

1.3 四种组合:一张表说清楚

同步和异步说的是交互方式,阻塞和非阻塞说的是调用者状态。两两组合,正好四种情况。

组合 说明 举例
同步阻塞 发起调用,等结果,期间啥也不干 早期Java的BIO,一条连接一个线程
同步非阻塞 发起调用,立刻返回,但得自己反复检查结果 轮询模式,比如select/poll
异步阻塞 等通知,但等待方式是阻塞的 Future.get() 设了超时,在那等着结果
异步非阻塞 发起调用就走,结果好了被通知,期间完全自由 回调函数、Python asyncio、Node.js

这里面最理想的是异步非阻塞。SDK集成里的回调改造,最终目标就是让调用方处于这种状态——发起请求后该干嘛干嘛,等SDK处理完了,通过回调把结果送回来。

1.4 为什么这个基础对SDK改造这么重要?

你可能在想:我就改个回调接口,搞清楚这些能干嘛?

用处大了。

  • 你对接的第三方SDK,提供的接口可能是同步阻塞的。你想让它变异步,就得先知道它在阻塞什么。
  • 你写的回调函数,如果在里面做了阻塞操作,那本质上又把异步变成了同步。
  • 你设计的回调接口,到底是传结果还是传异常?这取决于你对异步模型的理解。

嗯,我曾经见过一个团队,在回调函数里写了一个sleep(10)。结果整个异步框架的线程池被堵死,连心跳包都发不出去。事后复盘,根源就是没搞清楚"阻塞"在异步模型中的危害。

一句话总结:异步解决的是"不等结果就能继续"的问题。回调是异步的一种实现方式。但如果你在回调里搞阻塞,就等于换了个姿势等而已。

1.5 一张图看懂核心关系

下面这张SVG图,把同步异步、阻塞非阻塞的四种状态,以及它们和回调机制的关系,画在了一起。建议你保存下来,后面每章都回来看一眼。

同步/异步 × 阻塞/非阻塞 核心关系图 同步阻塞 调用后一直等 CPU空转,资源浪费 例:传统Socket读 ❌ 不适合SDK异步化 同步非阻塞 调用立刻返回 但需要自己轮询检查 例:非阻塞IO + while ⚠ 轮询消耗CPU 异步阻塞 发起后不等,但等待结果时阻塞 例:Future.get() 设超时 🔄 容易误用,不彻底 异步非阻塞 ✅ 发起就走,结果到了被通知 回调/事件驱动机制 ★ SDK回调改造目标 交互方式 交互方式 调用者状态 调用者状态 核心结论:异步非阻塞(右下角)是回调改造追求的理想模型 回调函数内部不要做阻塞操作,否则会退化为其他状态

这章的脉络就是这样了。掌握了同步异步、阻塞非阻塞这组概念,后面讲回调接口设计、线程模型、事件循环,你才能跟上节奏。