差别
这里会显示出您选择的修订版和当前版本之间的差别。
| 两侧同时换到之前的修订记录 前一修订版 | |||
| public:lang:async [2026/05/07 15:08] – 移除 - 外部编辑 (未知日期) 127.0.0.1 | public:lang:async [2026/05/07 15:08] (当前版本) – ↷ 页面public:it:async被移动至public:lang:async oakfire | ||
|---|---|---|---|
| 行 1: | 行 1: | ||
| + | ====== Async 异步编程 ====== | ||
| + | ===== 发展 ===== | ||
| + | * [[https:// | ||
| + | * 操作系统线程开销不菲:在当代 Linux 系统中,每个线程都会为其栈保留虚拟地址空间,创建一个线程需要消耗数十微秒的时间。上下文切换发生在内核空间,会燃烧 CPU 周期,而 O(n) 就绪状态轮询(如 select、poll)在大规模场景下会不断叠加开销。当一台服务器需要处理数千个并发连接时,若采用每连接一线程的架构,意味着数千个线程都将占用内存并竞争调度资源。系统将大量时间耗费在线程管理上,而这些资源本可用于执行更有价值的实际工作。这就是 C10K 问题,由丹·凯格尔于1999年提出。如果你正在构建一个网络服务器、聊天系统或任何需要处理大量并发连接的系统,你就需要一种方式来实现并发处理,而无需为每个连接分配一个线程 | ||
| + | * 解决方式一:回调函数 **Callbacks**: | ||
| + | * 与其等待I/ | ||
| + | * 缺点:回调颠倒了控制流;'' | ||
| + | * 缺点:破坏了错误处理的完整性。每个回调函数都需要独立的错误处理路径。由于不存在调用栈(回调函数在其注册上下文之外运行),错误无法自然地沿调用链向上传播。在回调链中处理部分失败的情况,意味着需要在链条中的每个函数中传递错误状态 | ||
| + | * 缺点:回调函数无法实现取消操作。当启动一个异步操作后,若决定不再需要其结果,通常没有办法将其终止。回调最终仍会被触发,而你的代码需要处理" | ||
| + | * 解决方式二:**Promises** and **Futures**: | ||
| + | * 这是一种承诺(JavaScript)或未来值(Java、Rust等)。这一概念可追溯到1977年Baker和Hewitt的研究,但直到2010年代的C10K压力才将其推入主流编程。JavaScript在ES2015中按照社区驱动的Promises/ | ||
| + | * 优点:Promise 具有可组合性:'' | ||
| + | * 优点:错误处理也更集中:在链式调用的末尾添加一个 `.catch()` 即可捕获任意步骤的失败 | ||
| + | * 缺点:Promise(承诺)是一次性的。一个 Promise 只会被决议一次。这使得它们不适合用于建模流、事件、重复消息或任何持续性的通信。例如,接收一系列消息的 WebSocket 并不符合“一个将在未来存在的值”这一模式 | ||
| + | * 缺点:组合方式相对笨拙 | ||
| + | * 缺点:错误静默消失。JavaScript 中的 Promise 若未通过 '' | ||
| + | * 缺点:类型分裂。每个函数现在可能返回一个值,也有可能返回一个 Promise。因此,调用者需要知道是哪一种,而库也需要决定提供哪一种。 | ||
| + | * 解决方式三:**Async/ | ||
| + | * async/ | ||
| + | * 优点:让异步代码看起来像顺序执行 | ||
| + | * Paying the Function Coloring Tax:鲍勃·奈斯特罗姆发表了《你的函数是什么颜色?》一文。这篇文章设想了一门语言,其中每个函数要么是" | ||
| + | * 这是对async/ | ||
| + | * 缺点:函数类型分裂:同步函数与异步函数,调用者需要依此区分设计模式与使用。一个同步思维设计模式下的项目,一旦启用了一个异步库,意味着一长串函数的修改。 | ||
| + | * 缺点:库的割裂:库和接口的提供者需要决定使用同步还是异步。在Python中,requests库(同步)和aiohttp(异步)是由不同开发者独立维护的两个项目,功能却完全相同。httpx最终实现了从同一个包中同时提供两种接口,然而这种改进之所以必要,正是由于同步与异步的人为割裂。 | ||
| + | * 缺点:新型 bug。O' | ||
| + | * 缺点:顺序陷阱:与 | ||
| + | * Go 语言刻意采用了 **goroutine**,通过接受更重的运行时负担,彻底避免了函数着色问题。(Go 实际上通过 context.Context 引入了一种着色形式,该上下文通过调用传播以实现取消操作) | ||
| + | * Java 的 '' | ||
| + | * Zig 更进一步:它完全移除了编译器层面的 async/ | ||