关于 m07-concurrency
面向 Rust 开发者的并发技能,帮助在多线程、异步和数据共享模式之间做选择。它结合工作负载与 Send/Sync 要求,说明实现方案及常见错误的处理思路。
主要功能
- 01区分 CPU 密集型与 I/O 密集型工作
- 02选择线程或异步模式
- 03比较共享与消息传递方式
- 04处理 Send/Sync 和异步错误
适用场景
- 01为工作负载选择并发方案
- 02解决 Send 或 Sync 编译错误
- 03检查锁使用与死锁风险
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ actionbook/ rust-skills --skill m07-concurrency安装后在 Claude Code 中输入/m07-concurrency确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
comparison.mdexamples/thread-patterns.mdpatterns/async-patterns.mdcommon-errors.md详细介绍
并发
第 1 层:语言机制
核心问题
这是 CPU 密集型还是 I/O 密集型?共享模型是什么?
选择并发原语之前:
- 工作负载类型是什么?
- 哪些数据需要共享?
- 线程安全要求是什么?
错误 → 设计问题
| 错误 | 不要只说 | 应该问 |
|---|---|---|
| E0277 Send | “添加 Send 约束” | 这种类型应该跨线程传递吗? |
| E0277 Sync | “用 Mutex 包装” | 真的需要共享访问吗? |
| Future 不是 Send | “使用 spawn_local” | 选择异步是正确的吗? |
| 死锁 | “调整锁的顺序” | 锁定设计正确吗? |
思考提示
添加并发之前:
-
工作负载是什么?
- CPU 密集型 → 线程(std::thread、rayon)
- I/O 密集型 → 异步(tokio、async-std)
- 混合型 → 混合方案
-
共享模型是什么?
- 无共享 → 消息传递(通道)
- 不可变共享 → Arc
- 可变共享 → Arc<Mutex> 或 Arc<RwLock>
-
有哪些 Send/Sync 要求?
- 跨线程所有权 → Send
- 跨线程引用 → Sync
- 单线程异步 → spawn_local
向上追溯 ↑(强制要求)
重要:不要只修复错误。要向上追溯,找出领域约束。
领域检测表
| 上下文关键词 | 加载领域技能 | 关键约束 |
|---|---|---|
| Web API、HTTP、axum、actix、处理程序 | domain-web | 处理程序可能在任意线程上运行 |
| 交易、支付、trading、payment | domain-fintech | 审计 + 线程安全 |
| gRPC、kubernetes、微服务 | domain-cloud-native | 分布式追踪 |
| CLI、终端、clap | domain-cli | 通常单线程即可 |
示例:Web API + Rc 错误
"Rc cannot be sent between threads" in Web API context
↑ DETECT: "Web API" → Load domain-web
↑ FIND: domain-web says "Shared state must be thread-safe"
↑ FIND: domain-web says "Rc in state" is Common Mistake
↓ DESIGN: Use Arc<T> with State extractor
↓ IMPL: axum::extract::State<Arc<AppConfig>>
通用追溯
"Send not satisfied for my type"
↑ Ask: What domain is this? Load domain-* skill
↑ Ask: Does this type need to cross thread boundaries?
↑ Check: m09-domain (is the data model correct?)
| 情况 | 追溯至 | 问题 |
|---|---|---|
| Web 中的 Send/Sync | domain-web | 状态管理模式是什么? |
| CLI 中的 Send/Sync | domain-cli | 真的需要多线程吗? |
| Mutex 与通道的选择 | m09-domain | 共享状态还是消息传递? |
| 异步与线程的选择 | m10-performance | 工作负载特征是什么? |
向下追溯 ↓
从设计到实现:
"Need parallelism for CPU work"
↓ Use: std::thread or rayon
"Need concurrency for I/O"
↓ Use: async/await with tokio
"Need to share immutable data across threads"
↓ Use: Arc<T>
"Need to share mutable data across threads"
↓ Use: Arc<Mutex<T>> or Arc<RwLock<T>>
↓ Or: channels for message passing
"Need simple atomic operations"
↓ Use: AtomicBool, AtomicUsize, etc.
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>> | 是 | 是 | 跨线程共享可变状态 |
决策流程图
What type of work?
├─ CPU-bound → std::thread or rayon
├─ I/O-bound → async/await
└─ Mixed → hybrid (spawn_blocking)
Need to share data?
├─ No → message passing (channels)
├─ Immutable → Arc<T>
└─ Mutable →
├─ Read-heavy → Arc<RwLock<T>>
└─ Write-heavy → Arc<Mutex<T>>
└─ Simple counter → AtomicUsize
Async context?
├─ Type is Send → tokio::spawn
├─ Type is !Send → spawn_local
└─ Blocking code → spawn_blocking
常见错误
| 错误 | 原因 | 修复方法 |
|---|---|---|
E0277 不满足 Send | 异步代码中包含非 Send 类型 | 使用 Arc 或 spawn_local |
E0277 不满足 Sync | 共享了非 Sync 类型 | 用 Mutex 包装 |
| 死锁 | 锁顺序问题 | 保持锁顺序一致 |
future is not Send | await 期间存在非 Send 类型 | 在 await 前释放 |
await 期间持有 MutexGuard | 挂起期间仍持有锁 | 正确限定锁的作用域 |
反模式
| 反模式 | 不妥之处 | 更好的做法 |
|---|---|---|
| 到处使用 Arc<Mutex> | 竞争、复杂度高 | 消息传递 |
| 在异步代码中使用 thread::sleep | 阻塞执行器 | tokio::time::sleep |
| 跨 await 持有锁 | 阻塞其他任务 | 严格限制锁的作用域 |
| 忽视死锁风险 | 难以调试 | 锁顺序、try_lock |
异步专属模式
避免跨 Await 持有 MutexGuard
// Bad: guard held across await
let guard = mutex.lock().await;
do_async().await; // guard still held!
// Good: scope the lock
{
let guard = mutex.lock().await;
// use guard
} // guard dropped
do_async().await;
异步代码中的非 Send 类型
// Rc is !Send, can't cross await in spawned task
// Option 1: use Arc instead
// Option 2: use spawn_local (single-thread runtime)
// Option 3: ensure Rc is dropped before .await
Related Skills
| When | See |
|---|---|
| Smart pointer choice | m02-resource |
| Interior mutability | m03-mutability |
| Performance tuning | m10-performance |
| Domain concurrency needs | domain-* |
评论
支持 Markdown