编程同步与异步原理详细讲解
·
一、核心概念对比
同步 (Synchronous) 异步 (Asynchronous) ┌──────────┐ ┌──────────┐ │ 任务 A │ │ 任务 A │ │ (执行中) │ │ (执行中) │ │ ...... │ ← 阻塞等待 │ 发起任务B │──→ 任务B在后台执行 │ 任务 B │ │ 继续执行 │ ┌──────────┐ │ (执行中) │ │ 其他工作 │ │ 任务B完成 │ │ ...... │ │ ...... │ │ 回调通知 │ │ 任务 C │ │ ← 处理结果 │←───└──────────┘ └──────────┘ └──────────┘ 串行执行 并发执行
二、同步(Synchronous)
2.1 基本原理
调用方发起请求后,必须等待结果返回才能继续执行下一步。
import time
def task_a():
print("任务A开始")
time.sleep(2) # 模拟耗时操作
print("任务A完成")
def task_b():
print("任务B开始")
time.sleep(3) # 模拟耗时操作
print("任务B完成")
# 同步执行:总耗时 = 2 + 3 = 5秒
start = time.time()
task_a() # 必须等A完成
task_b() # 才能开始B
print(f"总耗时: {time.time() - start:.1f}秒") # 约5秒
2.2 执行时间线
时间轴: 0s -------- 2s -------- 5s
|← 任务A →|← 任务B →|
| 阻塞中 | 阻塞中 |
CPU状态: [忙][等待][等待][忙][等待][等待][等待]
2.3 同步的特点
| 特点 | 说明 |
|---|---|
| ✅ 代码简单 | 自上而下,逻辑清晰 |
| ✅ 调试方便 | 单步跟踪,容易排查 |
| ✅ 顺序确定 | 执行顺序可预测 |
| ❌ 效率低下 | CPU在I/O等待时空闲 |
| ❌ 阻塞线程 | 一个慢操作卡住整个流程 |
三、异步(Asynchronous)
3.1 基本原理
调用方发起请求后,不等待结果,继续执行后续操作。结果通过回调/通知/轮询获取。
import asyncio
import time
async def task_a():
print("任务A开始")
await asyncio.sleep(2) # 非阻塞等待,让出控制权
print("任务A完成")
async def task_b():
print("任务B开始")
await asyncio.sleep(3) # 非阻塞等待,让出控制权
print("任务B完成")
# 异步执行:总耗时 = max(2, 3) = 3秒
async def main():
start = time.time()
await asyncio.gather(task_a(), task_b()) # 并发执行
print(f"总耗时: {time.time() - start:.1f}秒") # 约3秒
asyncio.run(main())
3.2 执行时间线
时间轴: 0s -------- 2s --- 3s
|←── 任务A ──→|
|←──── 任务B ────→|
任务A和B同时进行,总时间取最长的那个
四、异步的实现方式(由浅入深)
4.1 回调函数(Callback)
// JavaScript 经典回调模式
console.log("1. 开始");
// 异步读取文件
fs.readFile('data.txt', 'utf8', function(err, data) {
if (err) {
console.log("读取失败:", err);
return;
}
console.log("3. 文件内容:", data);
});
console.log("2. 继续执行,不等待文件读取");
// 输出顺序: 1 → 2 → 3
回调地狱问题:
// 😱 多层嵌套 —— "回调地狱"
getUser(userId, function(user) {
getOrders(user.id, function(orders) {
getOrderDetail(orders[0].id, function(detail) {
getProduct(detail.productId, function(product) {
console.log(product.name);
// 越嵌越深...
});
});
});
});
4.2 Promise(承诺)
// Promise 链式调用 —— 解决回调地狱
getUser(userId)
.then(user => getOrders(user.id))
.then(orders => getOrderDetail(orders[0].id))
.then(detail => getProduct(detail.productId))
.then(product => console.log(product.name))
.catch(err => console.error("出错了:", err));
Promise 原理图:
Promise 三种状态:
┌─────────┐ resolve(value) ┌───────────┐
│ Pending │ ──────────────────→ │ Fulfilled │ → .then(onFulfilled)
│ (等待中) │ │ (已完成) │
└────┬────┘ └───────────┘
│
│ reject(reason) ┌───────────┐
└─────────────────────────→ │ Rejected │ → .catch(onRejected)
│ (已拒绝) │
└───────────┘
状态一旦改变,不可逆转!
4.3 async/await(语法糖)
// async/await —— 让异步代码看起来像同步
async function fetchUserProduct(userId) {
try {
const user = await getUser(userId); // 等待,但不阻塞线程
const orders = await getOrders(user.id);
const detail = await getOrderDetail(orders[0].id);
const product = await getProduct(detail.productId);
console.log(product.name);
} catch (err) {
console.error("出错了:", err);
}
}
4.4 进化历程总结
回调函数 ──→ Promise ──→ async/await ↓ ↓ ↓ 嵌套地狱 链式调用 同步写法 难以维护 较为清晰 最为直观
五、底层机制:事件循环(Event Loop)
5.1 JavaScript 事件循环
┌───────────────────────────────────────────┐
│ 调用栈 (Call Stack) │
│ ┌─────────────────────────────────────┐ │
│ │ 同步代码在这里按顺序执行 │ │
│ └─────────────────────────────────────┘ │
└──────────────────┬────────────────────────┘
│ 遇到异步操作
▼
┌───────────────────────────────────────────┐
│ Web APIs / Node APIs │
│ ┌──────┐ ┌──────────┐ ┌───────────────┐ │
│ │Timer │ │ HTTP请求 │ │ 文件I/O │ │
│ └──┬───┘ └────┬─────┘ └──────┬────────┘ │
│ │ │ │ │
└─────┼──────────┼───────────────┼───────────┘
│ │ │
▼ ▼ ▼
┌───────────────────────────────────────────┐
│ 任务队列 (Task Queue) │
│ ┌─────────────────────────────────────┐ │
│ │ 微任务队列: Promise.then, async/await│ │ ← 优先级高
│ ├─────────────────────────────────────┤ │
│ │ 宏任务队列: setTimeout, setInterval │ │ ← 优先级低
│ └─────────────────────────────────────┘ │
└──────────────────┬────────────────────────┘
│ 调用栈空时
│ 事件循环取出任务
▼
┌──────────────┐
│ Event Loop │ ←── 不断循环检查
│ (事件循环) │
└──────────────┘
5.2 事件循环执行示例
console.log('1'); // 同步
setTimeout(() => console.log('2'), 0); // 宏任务
Promise.resolve().then(() => console.log('3')); // 微任务
console.log('4'); // 同步
// 输出顺序: 1 → 4 → 3 → 2
执行过程分析:
Step 1: 执行同步代码
调用栈: console.log('1') → 输出 1
Step 2: 遇到 setTimeout
调用栈: setTimeout(...) → 回调放入【宏任务队列】
Step 3: 遇到 Promise.then
调用栈: Promise.then(...) → 回调放入【微任务队列】
Step 4: 执行同步代码
调用栈: console.log('4') → 输出 4
Step 5: 调用栈空,先清空微任务队列
调用栈: () => console.log('3') → 输出 3
Step 6: 微任务清空,执行宏任务
调用栈: () => console.log('2') → 输出 2
六、Python 异步详解
6.1 协程(Coroutine)原理
import asyncio
async def cook_rice():
"""煮饭 - 耗时30分钟但不需要人盯着"""
print("🍚 开始煮饭...")
await asyncio.sleep(3) # 模拟30分钟,让出控制权
print("🍚 饭煮好了!")
return "米饭"
async def cook_dish():
"""炒菜 - 耗时15分钟"""
print("🥘 开始炒菜...")
await asyncio.sleep(1.5) # 模拟15分钟
print("🥘 菜炒好了!")
return "红烧肉"
async def make_soup():
"""煲汤 - 耗时20分钟"""
print("🍲 开始煲汤...")
await asyncio.sleep(2) # 模拟20分钟
print("🍲 汤煲好了!")
return "鸡汤"
async def main():
"""
同步做法: 30 + 15 + 20 = 65分钟
异步做法: max(30, 15, 20) = 30分钟 (同时进行)
"""
start = time.time()
# 三个任务并发执行
results = await asyncio.gather(
cook_rice(),
cook_dish(),
make_soup()
)
print(f"\n晚餐准备好了: {results}")
print(f"总耗时: {time.time() - start:.1f}秒") # 约3秒
asyncio.run(main())
6.2 协程调度原理
事件循环 (Event Loop) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 时间 0.0s: ├── cook_rice() 开始 → 遇到 await sleep(3) → 挂起,注册3秒后唤醒 ├── cook_dish() 开始 → 遇到 await sleep(1.5) → 挂起,注册1.5秒后唤醒 └── make_soup() 开始 → 遇到 await sleep(2) → 挂起,注册2秒后唤醒 时间 1.5s: └── cook_dish() 被唤醒 → 继续执行 → 完成 ✓ 时间 2.0s: └── make_soup() 被唤醒 → 继续执行 → 完成 ✓ 时间 3.0s: └── cook_rice() 被唤醒 → 继续执行 → 完成 ✓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
七、Java 异步编程
7.1 多线程方式
public class AsyncExample {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
// 创建异步任务
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(2000); } catch (Exception e) {}
return "数据库查询结果";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(3000); } catch (Exception e) {}
return "API调用结果";
});
// 等待所有任务完成
CompletableFuture.allOf(future1, future2).join();
System.out.println(future1.get()); // 数据库查询结果
System.out.println(future2.get()); // API调用结果
long elapsed = System.currentTimeMillis() - start;
System.out.println("总耗时: " + elapsed + "ms"); // 约3000ms
}
}
7.2 CompletableFuture 链式调用
CompletableFuture
.supplyAsync(() -> getUser(userId)) // 异步获取用户
.thenApplyAsync(user -> getOrders(user.getId())) // 异步获取订单
.thenApplyAsync(orders -> processOrders(orders)) // 异步处理订单
.thenAccept(result -> System.out.println(result)) // 消费结果
.exceptionally(throwable -> { // 异常处理
System.err.println("Error: " + throwable.getMessage());
return null;
});
八、I/O 模型对比(操作系统层面)
8.1 五种 I/O 模型
1. 同步阻塞 I/O (Blocking I/O)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
应用程序 │ 系统调用 │ 内核
│──────────→│
│ 阻塞等待 │ 准备数据...
│ │ 数据就绪
│ │ 拷贝数据到用户空间
│←──────────│
│ 处理数据 │
2. 同步非阻塞 I/O (Non-blocking I/O)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
应用程序 │ 系统调用 │ 内核
│──────────→│
│←─ EAGAIN ─│ 数据没准备好
│ 做其他事 │
│──────────→│ 轮询
│←─ EAGAIN ─│ 还没好
│ 做其他事 │
│──────────→│
│←──────────│ 数据就绪,返回数据
3. I/O 多路复用 (select/poll/epoll)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
应用程序 │ select() │ 内核
│──────────→│ 监控多个fd
│ 阻塞等待 │
│←──────────│ 某个fd就绪
│ read() │
│──────────→│
│←──────────│ 返回数据
4. 信号驱动 I/O
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
应用程序 │ 注册信号 │ 内核
│──────────→│
│ 做其他事 │ 准备数据...
│ │ 数据就绪
│←─ SIGIO ──│ 信号通知
│ read() │
│──────────→│
│←──────────│ 返回数据
5. 异步 I/O (AIO) — 真正的异步
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
应用程序 │ aio_read() │ 内核
│───────────→│
│ 做其他事 │ 准备数据...
│ 做其他事 │ 拷贝数据到用户空间
│←───────────│ 完成通知 + 数据已就绪
│ 直接用数据 │
8.2 对比总结
等待数据阶段 数据拷贝阶段 同步阻塞 阻塞 阻塞 最简单但最慢 同步非阻塞 非阻塞(轮询) 阻塞 CPU浪费在轮询 I/O多路复用 阻塞(在select) 阻塞 可监控多个连接 ★ 信号驱动 非阻塞 阻塞 较少使用 异步I/O 非阻塞 非阻塞 真正异步 ★
九、实际应用场景
9.1 Web 服务器处理请求
# ❌ 同步处理 —— 一次只能处理一个请求
from flask import Flask
import requests
app = Flask(__name__)
@app.route('/user/<id>')
def get_user_info(id):
# 每个请求都要等待,后面的请求排队
user = requests.get(f'http://user-service/users/{id}').json() # 等100ms
orders = requests.get(f'http://order-service/orders?uid={id}').json() # 等200ms
score = requests.get(f'http://score-service/scores/{id}').json() # 等150ms
# 总计: 450ms
return {"user": user, "orders": orders, "score": score}
# ✅ 异步处理 —— 并发处理多个请求
from aiohttp import web
import aiohttp
async def get_user_info(request):
id = request.match_info['id']
async with aiohttp.ClientSession() as session:
# 三个请求并发发出
tasks = [
session.get(f'http://user-service/users/{id}'),
session.get(f'http://order-service/orders?uid={id}'),
session.get(f'http://score-service/scores/{id}'),
]
responses = await asyncio.gather(*tasks)
user, orders, score = [await r.json() for r in responses]
# 总计: max(100, 200, 150) = 200ms 🚀 快了一倍多
return web.json_response({"user": user, "orders": orders, "score": score})
9.2 前端 UI 交互
// ❌ 同步请求 —— 页面卡死
function loadData() {
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data', false); // false = 同步
xhr.send(); // 🔒 页面冻结,用户无法操作
return JSON.parse(xhr.responseText);
}
// ✅ 异步请求 —— 页面流畅
async function loadData() {
showLoading(); // 显示加载动画
try {
const response = await fetch('/api/data'); // 异步请求
const data = await response.json();
renderTable(data); // 渲染数据
} catch (error) {
showError(error.message);
} finally {
hideLoading(); // 隐藏加载动画
}
}
// 页面始终可以响应用户操作 ✨
十、并发模型全景图
并发编程
┌───────┴───────┐
多线程 异步I/O
┌─────┴─────┐ │
抢占式 协作式 事件驱动
调度 调度 单线程
│ │ │
OS线程/ 协程 Event Loop
Java Thread Goroutine + 回调/协程
Python async
┌─────────────────────────────────────────────────────┐
│ 适用场景对比 │
├─────────────┬──────────────┬────────────────────────┤
│ 多线程 │ 多进程 │ 异步 I/O │
├─────────────┼──────────────┼────────────────────────┤
│ CPU密集+I/O │ CPU密集计算 │ I/O密集(网络/文件) │
│ 共享内存 │ 进程隔离 │ 单线程高并发 │
│ 锁的复杂度 │ 内存开销大 │ 无需锁,无竞争 │
│ C++/Java │ Python多核 │ Node.js/Python asyncio │
└─────────────┴──────────────┴────────────────────────┘
十一、常见误区
❌ 误区1: "异步一定比同步快" → 如果是纯CPU计算(没有I/O等待),异步没有优势,反而有调度开销 ❌ 误区2: "异步 = 多线程" → 异步可以在单线程上实现(如Node.js事件循环) ❌ 误区3: "async/await 会创建新线程" → async/await 只是协程调度,通常在同一线程内切换 ❌ 误区4: "用了异步就不会阻塞" → 在异步代码中调用同步阻塞API(如time.sleep), 仍然会阻塞整个事件循环!应使用 await asyncio.sleep() ✅ 关键认知: → 异步的核心价值: 在等待I/O时,CPU去做其他事情 → 适用场景: 高并发、I/O密集型任务 → 不适用: 纯计算密集型任务
十二、一句话总结
┌─────────────────────────────────────────────────────────┐ │ │ │ 同步 = 排队买奶茶,你站在柜台前等做好才走 │ │ │ │ 异步 = 扫码下单买奶茶,拿个号去逛街,做好了微信通知你 │ │ │ │ 核心区别: 等待期间,你(CPU)能不能去做别的事 │ │ │ └─────────────────────────────────────────────────────────┘
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)