m07-并发Skill m07-concurrency

这是一个关于Rust编程语言并发与异步编程的专家指南。它提供了并发编程的核心问题分析、错误诊断、设计决策流程、最佳实践和反模式。内容涵盖CPU密集型与I/O密集型任务的选择、Send/Sync标记、线程安全、消息传递、锁机制、异步编程模式以及如何避免死锁和竞态条件。关键词包括:Rust并发、异步编程、线程安全、Send/Sync、Mutex、通道、tokio、死锁、竞态条件、性能优化。

后端开发 0 次安装 0 次浏览 更新于 2/27/2026

名称: m07-并发 描述: “关键:用于并发/异步。触发词:E0277 Send Sync,无法在线程间发送,线程,spawn,通道,mpsc,Mutex,RwLock,Atomic,async,await,Future,tokio,死锁,竞态条件,并发,线程,异步,死锁” 用户可调用: false

并发

第一层:语言机制

核心问题

这是CPU密集型还是I/O密集型,共享模型是什么?

在选择并发原语之前:

  • 工作负载类型是什么?
  • 需要共享什么数据?
  • 线程安全要求是什么?

错误 → 设计问题

错误 不要只说 应该问
E0277 Send “添加Send约束” 这个类型需要跨线程吗?
E0277 Sync “用Mutex包装” 真的需要共享访问吗?
Future 不是 Send “使用 spawn_local” 异步是正确选择吗?
死锁 “重新排序锁” 锁的设计正确吗?

思考提示

在添加并发之前:

  1. 工作负载是什么?

    • CPU密集型 → 线程 (std::thread, rayon)
    • I/O密集型 → 异步 (tokio, async-std)
    • 混合型 → 混合方法
  2. 共享模型是什么?

    • 不共享 → 消息传递 (通道)
    • 不可变共享 → Arc<T>
    • 可变共享 → Arc<Mutex<T>> 或 Arc<RwLock<T>>
  3. Send/Sync要求是什么?

    • 跨线程所有权 → Send
    • 跨线程引用 → Sync
    • 单线程异步 → spawn_local

向上追溯 ↑ (强制要求)

关键:不要只修复错误。向上追溯以找到领域约束。

领域检测表

上下文关键词 加载领域技能 关键约束
Web API, HTTP, axum, actix, handler domain-web 处理器在任何线程上运行
交易, 支付, trading, payment domain-fintech 审计 + 线程安全
gRPC, kubernetes, microservice domain-cloud-native 分布式追踪
CLI, terminal, clap domain-cli 通常单线程即可

示例:Web API + Rc 错误

"Rc 无法在线程间发送" 在 Web API 上下文中
    ↑ 检测:"Web API" → 加载 domain-web
    ↑ 发现:domain-web 说 "共享状态必须是线程安全的"
    ↑ 发现:domain-web 说 "状态中的 Rc" 是常见错误
    ↓ 设计:使用 Arc<T> 和 State 提取器
    ↓ 实现:axum::extract::State<Arc<AppConfig>>

通用追溯

"我的类型不满足 Send"
    ↑ 问:这是什么领域?加载 domain-* 技能
    ↑ 问:这个类型需要跨越线程边界吗?
    ↑ 检查:m09-domain (数据模型正确吗?)
情况 追溯到 问题
Web 中的 Send/Sync domain-web 状态管理模式是什么?
CLI 中的 Send/Sync domain-cli 真的需要多线程吗?
Mutex 与通道 m09-domain 共享状态还是消息传递?
异步与线程 m10-performance 工作负载概况是什么?

向下追溯 ↓

从设计到实现:

"CPU工作需要并行性"
    ↓ 使用:std::thread 或 rayon

"I/O需要并发性"
    ↓ 使用:async/await 与 tokio

"需要在线程间共享不可变数据"
    ↓ 使用:Arc<T>

"需要在线程间共享可变数据"
    ↓ 使用:Arc<Mutex<T>> 或 Arc<RwLock<T>>
    ↓ 或者:用于消息传递的通道

"需要简单的原子操作"
    ↓ 使用:AtomicBool, AtomicUsize 等

Send/Sync 标记

标记 含义 示例
Send 可以在线程间转移所有权 大多数类型
Sync 可以在线程间共享引用 Arc<T>
!Send 必须留在一个线程上 Rc<T>
!Sync 不能跨线程共享引用 RefCell<T>

快速参考

模式 线程安全 阻塞 使用时机
std::thread CPU密集型并行
async/await I/O密集型并发
Mutex<T> 共享可变状态
RwLock<T> 读多写少的共享状态
mpsc::channel 可选 消息传递
Arc<Mutex<T>> 跨线程共享可变

决策流程图

工作类型是什么?
├─ CPU密集型 → std::thread 或 rayon
├─ I/O密集型 → async/await
└─ 混合型 → 混合方法 (spawn_blocking)

需要共享数据吗?
├─ 否 → 消息传递 (通道)
├─ 不可变 → Arc<T>
└─ 可变 →
   ├─ 读多写少 → Arc<RwLock<T>>
   └─ 写多读少 → Arc<Mutex<T>>
   └─ 简单计数器 → AtomicUsize

异步上下文?
├─ 类型是 Send → tokio::spawn
├─ 类型是 !Send → spawn_local
└─ 阻塞代码 → spawn_blocking

常见错误

错误 原因 修复方法
E0277 Send 不满足 异步中的非 Send 类型 使用 Arc 或 spawn_local
E0277 Sync 不满足 共享的非 Sync 类型 用 Mutex 包装
死锁 锁顺序 一致的锁顺序
future is not Send 非 Send 类型跨越 await 在 await 前释放
MutexGuard 跨越 await 挂起期间持有守卫 正确限定守卫作用域

反模式

反模式 为什么不好 更好的方法
到处使用 Arc<Mutex<T>> 竞争、复杂性 消息传递
异步中使用 thread::sleep 阻塞执行器 tokio::time::sleep
在 await 期间持有锁 阻塞其他任务 严格控制锁的作用域
忽略死锁风险 难以调试 锁顺序、try_lock

异步特定模式

避免 MutexGuard 跨越 Await

// 不好:守卫在 await 期间被持有
let guard = mutex.lock().await;
do_async().await;  // 守卫仍然被持有!

// 好:限定锁的作用域
{
    let guard = mutex.lock().await;
    // 使用守卫
}  // 守卫被释放
do_async().await;

异步中的非 Send 类型

// Rc 是 !Send,不能在生成的任务中跨越 await
// 选项 1:改用 Arc
// 选项 2:使用 spawn_local (单线程运行时)
// 选项 3:确保 Rc 在 .await 前被释放

相关技能

何时 参见
智能指针选择 m02-resource
内部可变性 m03-mutability
性能调优 m10-performance
领域并发需求 domain-*