返回文章列表

文章

我们在说同步/异步时到底是在说什么?

同步/异步 说的是「调用后是否等待完成」

目录
  1. 当我们讨论「同步」与「异步」时,核心是在说调用方发起一项操作后,是否要等这项操作完成才能继续执行下一步。下面分几个维度来说明它们的含义和典型使用场景。
  2. 一、概念区分
  3. 二、同步 vs 异步 的使用场景
  4. 1. 同步场景
  5. 2. 异步场景
  6. 三、同步/异步 指的到底是什么?
  7. 四、如何选?
  8. 举例
  9. let (s1, s2) = tokio::join!( tokio::fs::read_to_string("a.txt"), tokio::fs::read_to_string("b.txt") ); ```
  10. 五、总结
  11. 📎 参考文章

当我们讨论「同步」与「异步」时,核心是在说调用方发起一项操作后,是否要等这项操作完成才能继续执行下一步。下面分几个维度来说明它们的含义和典型使用场景。#

一、概念区分#

方面同步(Synchronous)异步(Asynchronous)
调用语义发起调用后,会**阻塞**调用者,直到操作完成并返回结果发起调用后**不阻塞**调用者,立即返回,操作在后台进行
线程行为同一线程负责整个操作,从开始到结束都在该线程上执行发起线程发出请求后可去做其他事,真正执行操作的可能是后台线程或事件回调
编程模型直接写:`let r = f(); // f 完成后才有结果`通过回调、Future/Promise、事件通知等拿到结果
资源占用阻塞期间可能浪费线程/CPU等待期间线程可复用、CPU 可执行别的任务

二、同步 vs 异步 的使用场景#

1. 同步场景#

  1. CPU 密集型计算
    • 例如矩阵乘法、图像渲染等,执行时间较长,但基本不会做 I/O。
    • 用同步更简单,数据在本地,阻塞线程做计算,直观且易于调试。
  2. 脚本型、一次性批处理
    • 如脚本按顺序执行任务,不需要并发或并行,写同步调用更直观。
  3. 事务性或串行依赖
    • 多步操作必须前一步完成才能进行下一步(如数据库事务中的多条 SQL 顺序执行)。

2. 异步场景#

  1. 高并发 I/O 密集型服务
    • 网络服务器(HTTP、WebSocket、Redis)、代理、爬虫等,大量连接/请求轮流等待网络数据。
    • 异步让单线程或少量线程能同时管理成千上万连接,节省上下文切换和线程资源。
  2. GUI 与事件驱动
    • 桌面/移动应用的界面线程需要保持流畅响应,任何耗时操作都应异步(后台线程或异步 API),否则界面卡死。
  3. 后台定时/并行任务
    • 比如同时向多个外部 API 发请求,用 Promise.alltokio::join! 并发调用,最后汇总结果。

三、同步/异步 指的到底是什么?#

  1. 调用者视角:
    • 同步:调用 foo(),会阻塞当前执行流,直到 foo 完成并返回。
    • 异步:调用 foo_async(),立即拿到一个“将来才会有结果”的句柄(Future/Promise/Callback),调用者可以继续下一步,稍后通过 .await、回调或事件处理拿到结果。
  2. 执行机制:
    • 同步 完全在线程栈上执行;
    • 异步 将操作注册到某种调度器(事件循环、线程池),由它在后台下发和唤醒。
  3. 阻塞 vs 挂起:
    • 阻塞(block)会让线程进入内核等待,无法进行任何其他工作;
    • 挂起(suspend)只是把当前任务挂起到调度队列,线程可以去执行别的任务。

四、如何选?#

考量维度选同步选异步
调用顺序必须严格×
逻辑非常简单✔(少量 I/O)
I/O 操作频繁×(线程多、切换多,资源浪费)✔(节省线程、响应高并发)
UI 或需保持响应×(易卡死)✔(后台执行,不影响主线程)
平台/语言支持通用需语言或框架支持异步模型(如 Tokio、Node.js、.NET async)

举例#

  • 同步(Python、Rust、Java 都可):

// 顺序读两个文件 let s1 = std::fs::read_to_string("a.txt")?; let s2 = std::fs::read_to_string("b.txt")?; ```

  • 异步(Rust Tokio 示例):

let (s1, s2) = tokio::join!( tokio::fs::read_to_string("a.txt"), tokio::fs::read_to_string("b.txt") ); ```#

五、总结#

  • 同步/异步 说的是「调用后是否等待完成」。
  • 同步 编程简单直观,但在 I/O 密集场景会浪费线程资源。
  • 异步 能高效利用线程、处理高并发 I/O,但对编程模型有一定复杂度。
  • 选择时应根据 业务特性(CPU-bound vs I/O-bound)并发需求平台支持 来决定。

📎 参考文章#