m07-concurrency について
Rust 開発者向けに、スレッド、非同期処理、データ共有の方式を選ぶスキルです。処理の種類や Send/Sync の要件に基づき、実装方法とよくあるエラーへの対処を整理します。
主な機能
- 01CPU バウンドと I/O バウンドの判別
- 02スレッドまたは非同期処理の選択
- 03共有とメッセージパッシング方式の比較
- 04Send/Sync と非同期処理のエラー対応
活用シーン
- 01処理に合った並行処理方式の選択
- 02Send または 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 not 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 | 1 つのスレッド上にとどめる必要がある | 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 の前にドロップする |
MutexGuard が await をまたぐ | サスペンド中にガードが保持されている | ガードのスコープを適切に設定する |
アンチパターン
| アンチパターン | 問題点 | より良い方法 |
|---|---|---|
| あらゆる箇所で 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 対応