rust-concurrency

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Concurrency vs Async

并发 vs 异步

DimensionConcurrency (threads)Async (async/await)
MemoryEach thread has separate stackSingle thread reused
BlockingBlocks OS threadDoesn't block, yields
Use caseCPU-intensiveI/O-intensive
ComplexitySimple and directRequires runtime
Key Insight: Threads for parallelism, async for concurrency.
维度并发(线程)异步(async/await)
内存每个线程拥有独立的栈复用单个线程
阻塞行为阻塞操作系统线程不阻塞,主动让出
适用场景CPU密集型I/O密集型
复杂度简单直接需要运行时支持
核心要点:线程用于并行处理,异步用于并发处理。

Send/Sync Quick Reference

Send/Sync快速参考

Send - Can Transfer Ownership Between Threads

Send - 可在线程间转移所有权

Basic types → automatically Send
Contains references → automatically Send
Raw pointers → NOT Send
Rc → NOT Send (non-atomic ref counting)
Rule: If all fields are Send, the type is Send.
基础类型 → 自动实现Send
包含引用的类型 → 自动实现Send
裸指针 → 未实现Send
Rc → 未实现Send(非原子引用计数)
规则:如果一个类型的所有字段都实现了Send,那么该类型自动实现Send。

Sync - Can Share References Between Threads

Sync - 可在线程间共享引用

&T where T: Sync → automatically Sync
RefCell → NOT Sync (runtime checking not thread-safe)
MutexGuard → NOT Sync (intentionally)
Rule:
&T
is Send if
T
is Sync.
&T(当T: Sync时)→ 自动实现Sync
RefCell → 未实现Sync(运行时检查不具备线程安全性)
MutexGuard → 未实现Sync(有意设计)
规则:若T实现了Sync,则&T自动实现Send。

Solution Patterns

解决方案模式

Pattern 1: Shared Mutable State

模式1:共享可变状态

rust
use std::sync::{Arc, Mutex};

let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];

for _ in 0..10 {
    let counter = Arc::clone(&counter);
    let handle = std::thread::spawn(move || {
        let mut num = counter.lock().unwrap();
        *num += 1;
    });
    handles.push(handle);
}

for handle in handles {
    handle.join().unwrap();
}
When to use: Multiple threads need to mutate shared data.
Trade-offs: Lock contention can limit scalability.
rust
use std::sync::{Arc, Mutex};

let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];

for _ in 0..10 {
    let counter = Arc::clone(&counter);
    let handle = std::thread::spawn(move || {
        let mut num = counter.lock().unwrap();
        *num += 1;
    });
    handles.push(handle);
}

for handle in handles {
    handle.join().unwrap();
}
适用场景:多个线程需要修改共享数据时。
权衡点:锁竞争可能会限制可扩展性。

Pattern 2: Message Passing

模式2:消息传递

rust
use std::sync::mpsc;

let (tx, rx) = mpsc::channel();

thread::spawn(move || {
    tx.send("hello").unwrap();
});

println!("{}", rx.recv().unwrap());
When to use: Threads communicate without shared state.
Trade-offs: Copy/move overhead for messages.
rust
use std::sync::mpsc;

let (tx, rx) = mpsc::channel();

thread::spawn(move || {
    tx.send("hello").unwrap();
});

println!("{}", rx.recv().unwrap());
适用场景:线程间无需共享状态即可通信时。
权衡点:消息存在复制/移动开销。

Pattern 3: Async Runtime (Tokio)

模式3:异步运行时(Tokio)

rust
use tokio;

#[tokio::main]
async fn main() {
    let handle = tokio::spawn(async {
        // Async task
        fetch_data().await
    });

    let result = handle.await.unwrap();
}
When to use: I/O-bound operations (network, filesystem).
Trade-offs: Requires async runtime, function coloring.
rust
use tokio;

#[tokio::main]
async fn main() {
    let handle = tokio::spawn(async {
        // 异步任务
        fetch_data().await
    });

    let result = handle.await.unwrap();
}
适用场景:I/O密集型操作(网络、文件系统)。
权衡点:需要异步运行时,存在函数着色问题。

Workflow

工作流程

Step 1: Choose Concurrency Model

步骤1:选择并发模型

CPU-intensive task?
  → Use threads (rayon for data parallelism)

I/O-intensive task?
  → Use async/await (tokio, async-std)

Both?
  → Use async with spawn_blocking for CPU work
CPU密集型任务?
  → 使用线程(数据并行可使用rayon)

I/O密集型任务?
  → 使用async/await(tokio、async-std)

两者兼具?
  → 使用异步,并通过spawn_blocking处理CPU密集型工作

Step 2: Determine Data Sharing Strategy

步骤2:确定数据共享策略

No shared state?
  → Message passing (mpsc channels)

Read-heavy shared state?
  → Arc<RwLock<T>>

Write-heavy shared state?
  → Arc<Mutex<T>> or lock-free alternatives

Simple counters/flags?
  → Atomic types (AtomicUsize, AtomicBool)
无共享状态?
  → 消息传递(mpsc通道)

读多写少的共享状态?
  → Arc<RwLock<T>>

写多读少的共享状态?
  → Arc<Mutex<T>>或无锁替代方案

简单计数器/标志位?
  → 原子类型(AtomicUsize、AtomicBool)

Step 3: Verify Thread Safety

步骤3:验证线程安全性

Check Send bounds
  → Can transfer ownership?

Check Sync bounds
  → Can share references?

Test for data races
  → Use miri, loom, or thread sanitizers
检查Send约束
  → 能否转移所有权?

检查Sync约束
  → 能否共享引用?

测试竞态条件
  → 使用miri、loom或线程sanitizer

Common Errors & Solutions

常见错误与解决方案

ErrorCauseSolution
E0277 Send not satisfiedContains non-Send typesCheck all fields, replace Rc with Arc
E0277 Sync not satisfiedShared reference type not SyncWrap with Mutex/RwLock
DeadlockInconsistent lock orderingEstablish and follow lock hierarchy
MutexGuard across awaitLock held while suspendedScope lock before await point
Data race (runtime)Improper synchronizationUse proper sync primitives
错误原因解决方案
E0277 Send约束不满足包含非Send类型检查所有字段,将Rc替换为Arc
E0277 Sync约束不满足共享引用类型未实现Sync用Mutex/RwLock包裹
死锁锁顺序不一致建立并遵循锁层级
await前后持有MutexGuard挂起时持有锁在await点之前限定锁的作用域
运行时竞态条件同步不当使用正确的同步原语

Deadlock Prevention

死锁预防

Rule 1: Consistent Lock Ordering

规则1:保持一致的锁顺序

rust
// Always lock A before B
let _lock_a = resource_a.lock();
let _lock_b = resource_b.lock();
// Never lock B before A elsewhere
rust
// 始终先锁A再锁B
let _lock_a = resource_a.lock();
let _lock_b = resource_b.lock();
// 其他地方绝不能先锁B再锁A

Rule 2: Minimize Lock Scope

规则2:最小化锁的作用域

rust
// ❌ Bad: lock held too long
let guard = data.lock();
do_work(&guard);
more_work();  // still locked

// ✅ Good: release early
{
    let guard = data.lock();
    do_work(&guard);
}  // lock released
more_work();
rust
// ❌ 错误:持有锁时间过长
let guard = data.lock();
do_work(&guard);
more_work();  // 此时仍持有锁

// ✅ 正确:提前释放锁
{
    let guard = data.lock();
    do_work(&guard);
}  // 锁已释放
more_work();

Rule 3: Avoid Locks Across Await

规则3:避免在await前后持有锁

rust
// ❌ Bad: lock across await
let guard = mutex.lock().unwrap();
async_call().await;  // DEADLOCK RISK

// ✅ Good: drop lock before await
let value = {
    let guard = mutex.lock().unwrap();
    guard.clone()
};  // lock dropped
async_call().await;
rust
// ❌ 错误:await前后持有锁
let guard = mutex.lock().unwrap();
async_call().await;  // 存在死锁风险

// ✅ 正确:在await之前释放锁
let value = {
    let guard = mutex.lock().unwrap();
    guard.clone()
};  // 锁已释放
async_call().await;

Performance Considerations

性能考量

StrategyWhen to UseTrade-offs
Fine-grained lockingLock small portionsMore complex, avoid contention
RwLockRead-heavy workloadsSlower writes than Mutex
AtomicsSimple counters/flagsLimited operations, no compound ops
Message passingAvoid shared stateCopy/move overhead
Lock-free structuresHigh contentionComplex, use crates (crossbeam)
策略适用场景权衡点
细粒度锁锁定小范围数据实现更复杂,需避免竞争
RwLock读密集型工作负载写入速度比Mutex慢
原子类型简单计数器/标志位操作有限,不支持复合操作
消息传递避免共享状态存在复制/移动开销
无锁结构高竞争场景实现复杂,可使用crossbeam等crate

Async-Specific Patterns

异步专属模式

Spawning Tasks

生成任务

rust
// Spawn independent task
tokio::spawn(async move {
    process_data(data).await
});

// Spawn with 'static requirement
tokio::spawn(async move {
    let data = Arc::clone(&data);  // Share ownership
    work_with(data).await
});
rust
// 生成独立任务
tokio::spawn(async move {
    process_data(data).await
});

// 生成满足'static约束的任务
tokio::spawn(async move {
    let data = Arc::clone(&data);  // 共享所有权
    work_with(data).await
});

Concurrent Operations

并发操作

rust
use tokio::join;

// Wait for all to complete
let (result1, result2, result3) = tokio::join!(
    fetch_user(),
    fetch_posts(),
    fetch_comments()
);

// First to complete
let result = tokio::select! {
    r = fetch_from_primary() => r,
    r = fetch_from_backup() => r,
};
rust
use tokio::join;

// 等待所有任务完成
let (result1, result2, result3) = tokio::join!(
    fetch_user(),
    fetch_posts(),
    fetch_comments()
);

// 取第一个完成的任务结果
let result = tokio::select! {
    r = fetch_from_primary() => r,
    r = fetch_from_backup() => r,
};

Timeout and Cancellation

超时与取消

rust
use tokio::time::{timeout, Duration};

match timeout(Duration::from_secs(5), long_operation()).await {
    Ok(result) => result,
    Err(_) => {
        // Operation timed out
    }
}
rust
use tokio::time::{timeout, Duration};

match timeout(Duration::from_secs(5), long_operation()).await {
    Ok(result) => result,
    Err(_) => {
        // 操作超时
    }
}

Review Checklist

审核检查清单

When reviewing concurrent code:
  • All shared data properly synchronized (Arc/Mutex/RwLock)
  • Send/Sync bounds satisfied for types crossing threads
  • No locks held across await points
  • Consistent lock ordering to prevent deadlocks
  • Appropriate choice between threads and async
  • Message passing channels used correctly (no deadlocks)
  • Atomic operations used for simple shared state
  • Thread pool sized appropriately for workload
  • Error handling for lock poisoning
  • Graceful shutdown and resource cleanup
审核并发代码时需检查:
  • 所有共享数据都已正确同步(使用Arc/Mutex/RwLock)
  • 跨线程的类型满足Send/Sync约束
  • 未在await前后持有锁
  • 保持一致的锁顺序以预防死锁
  • 合理选择线程与异步模型
  • 消息传递通道使用正确(无死锁)
  • 简单共享状态使用原子操作
  • 线程池大小与工作负载匹配
  • 处理锁中毒错误
  • 优雅关闭与资源清理

Verification Commands

验证命令

bash
undefined
bash
undefined

Check compilation with thread safety

检查线程安全性相关编译问题

cargo check
cargo check

Run tests with thread sanitizer (requires nightly)

使用线程sanitizer运行测试(需要nightly版本)

RUSTFLAGS="-Z sanitizer=thread" cargo +nightly test
RUSTFLAGS="-Z sanitizer=thread" cargo +nightly test

Test with miri (detect undefined behavior)

使用miri测试(检测未定义行为)

cargo +nightly miri test
cargo +nightly miri test

Use loom for exhaustive concurrency testing

使用loom进行全面并发测试

cargo test --features loom
cargo test --features loom

Check for race conditions

检查竞态条件

cargo clippy -- -W clippy::mutex_atomic
undefined
cargo clippy -- -W clippy::mutex_atomic
undefined

Common Pitfalls

常见陷阱

1. Rc in Multi-threaded Context

1. 多线程环境中使用Rc

Symptom: E0277 error, Rc<T> cannot be sent between threads
Fix: Replace
Rc
with
Arc
rust
// ❌ Bad
let data = Rc::new(value);
thread::spawn(move || { /* use data */ });

// ✅ Good
let data = Arc::new(value);
thread::spawn(move || { /* use data */ });
症状:出现E0277错误,Rc<T>无法在线程间传递
修复方案:将
Rc
替换为
Arc
rust
// ❌ 错误
let data = Rc::new(value);
thread::spawn(move || { /* 使用data */ });

// ✅ 正确
let data = Arc::new(value);
thread::spawn(move || { /* 使用data */ });

2. Lock Across Await Points

2. 在await前后持有锁

Symptom: Deadlock or "future cannot be sent between threads safely"
Fix: Drop lock before await
rust
// ❌ Bad
let guard = mutex.lock().unwrap();
async_fn().await;

// ✅ Good
let value = mutex.lock().unwrap().clone();
drop(guard);  // Explicit drop
async_fn().await;
症状:死锁或出现“future无法安全地在线程间传递”错误
修复方案:在await之前释放锁
rust
// ❌ 错误
let guard = mutex.lock().unwrap();
async_fn().await;

// ✅ 正确
let value = mutex.lock().unwrap().clone();
drop(guard);  // 显式释放锁
async_fn().await;

3. Missing Arc Clone

3. 未克隆Arc

Symptom: Borrow checker errors when spawning threads
Fix: Clone Arc before moving into closure
rust
// ❌ Bad
let data = Arc::new(vec![1, 2, 3]);
thread::spawn(move || { /* data moved */ });
// data is gone

// ✅ Good
let data = Arc::new(vec![1, 2, 3]);
let data_clone = Arc::clone(&data);
thread::spawn(move || { /* data_clone moved */ });
// data still available
症状:生成线程时出现借用检查器错误
修复方案:在将Arc移动到闭包之前进行克隆
rust
// ❌ 错误
let data = Arc::new(vec![1, 2, 3]);
thread::spawn(move || { /* data被移动 */ });
// data已不可用

// ✅ 正确
let data = Arc::new(vec![1, 2, 3]);
let data_clone = Arc::clone(&data);
thread::spawn(move || { /* data_clone被移动 */ });
// data仍可使用

Related Skills

相关技能

  • rust-async - Advanced async patterns (Stream, select, backpressure)
  • rust-async-pattern - Async architecture and design patterns
  • rust-ownership - Understanding ownership for thread safety
  • rust-mutability - Interior mutability patterns (Cell, RefCell)
  • rust-performance - Concurrency performance optimization
  • rust-unsafe - Writing safe concurrent abstractions
  • rust-async - 高级异步模式(Stream、select、背压)
  • rust-async-pattern - 异步架构与设计模式
  • rust-ownership - 理解所有权以保障线程安全
  • rust-mutability - 内部可变性模式(Cell、RefCell)
  • rust-performance - 并发性能优化
  • rust-unsafe - 编写安全的并发抽象

Localized Reference

本地化参考

  • Chinese version: SKILL_ZH.md - 完整中文版本,包含所有内容
  • 中文版本: SKILL_ZH.md - 完整中文版本,包含所有内容