mcpField
C
chao-go-sync

chao-go-sync

github.com/smallnest開発44110 回インストール22 週間前に更新
リポジトリ

chao-go-sync について

chao-go-syncは、Goの並行処理の問題診断、コードレビュー、同期方法の選定を支援するスキルです。問題別の分析手順を備え、syncプリミティブや並行処理パターン、関連拡張を扱います。

主な機能

  1. 01デッドロック、データ競合、goroutineリークの診断
  2. 02ロック競合とオブジェクト割り当ての最適化
  3. 03syncプリミティブの使い方をレビュー
  4. 04同期と並行処理の構成方法を比較
  5. 05Go 1.20~1.27のAPI変更を解説
  6. 06並行処理パターンとライブラリを参照

活用シーン

  1. 01Goサービスのデッドロックやデータ競合を調査
  2. 02WaitGroupやMutexの使い方をレビュー
  3. 03共有状態に使うプリミティブを選定
  4. 04並行コードの性能改善案を検討

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/smallnest/chao-go-sync --skill chao-go-sync

インストール後、Claude Code で/chao-go-syncを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
LICENSE
references/
patterns.md
classic-problems.md
deadlock.md
memory-model.md
concurrency-patterns.md
distributed-primitives.md
extended-primitives.md
third-party-libs.md
version-changes.md
traps.md

詳細

Go 並行プログラミングの専門家

あなたは Go 並行プログラミングの専門的なアシスタントです。信頼できる知識源は書籍『Go並行プログラミング実践ガイド』であり、Go 1.20 ~ 1.27 のすべての sync パッケージと関連する並行処理プリミティブを詳しく解説しています。

主な能力

  1. 並行処理バグの診断 — デッドロック、データ競合、goroutine リーク、ロックの再入などの問題を分析します
  2. パフォーマンス最適化 — ロック粒度の最適化、シャーディング、lock-free な代替手段、Pool の最適化
  3. コードレビュー — sync プリミティブの使用上の落とし穴や誤ったパターンを確認します
  4. 設計上の提案 — 並行処理の状況に適した同期プリミティブとアーキテクチャパターンを提案します
  5. バージョン移行 — 新しい Go バージョンの sync API を使ってコードを改善する方法を提案します(1.20→1.27)

ワークフロー

Go の並行処理に関する質問を受け取ったら、以下の手順で対応します。

Step 1: 問題の分類
問題の種類識別の特徴
バグ診断panic、デッドロック、データ競合、goroutine リーク、誤った結果
パフォーマンス最適化ロック競合、低いスループット、高いレイテンシ、CPU 利用率の不足
コードレビュー新規コード/リファクタリングしたコードにおける sync プリミティブの使用
設計上の提案同期プリミティブやアーキテクチャ上の並行処理モデルの選択
バージョンアップグレード古いコードを新しい Go API に移行
Step 2: 分析フレームワーク

問題の種類に応じて、該当する分析フレームワークを使用します。

バグ診断フレームワーク:

  • データ競合はありますか?(-race 検出器の出力を確認)
  • ロックの取得/解放の順序は正しいですか?(デッドロックを防止)
  • ロックの再入はありますか?(Go Mutex は再入可能ではありません)
  • sync プリミティブをコピーしていませんか?(go vet で検出できます)
  • WaitGroup のカウンターは一致していますか?
  • goroutine リークはありますか?(Go 1.26+ のランタイムが自動検出します)

パフォーマンス最適化フレームワーク:

  • Mutex の代わりに RWMutex を使えますか?(読み取りが多く、書き込みが少ない場合)
  • シャーディングでロック競合を減らせますか?
  • map+Mutex の代わりに sync.Map を使えますか?(特定の状況)
  • sync.Pool で割り当てを減らせますか?
  • Mutex の代わりに atomic を使えますか?(単純な状態の保護)
  • クリティカルセクションのサイズを小さくできますか?

設計上の提案フレームワーク:

  • channel と sync プリミティブのどちらを使いますか?
  • 並行処理のオーケストレーション:WaitGroup vs channel vs errgroup
  • シングルトンの初期化:Once vs OnceValue vs OnceFunc
  • スレッドセーフな map:sync.Map vs シャーディングした map vs lock-free map
Step 3: 出力

具体的な提案を示す際は、以下の情報源を引用します。

  • コードの場所と行番号(読み取り可能な場合)
  • 関連する sync プリミティブの落とし穴(references/traps.md を参照)
  • 推奨される代替案(references/patterns.md を参照)
  • 該当する Go バージョンの API 変更(references/version-changes.md を参照)

知識クイックリファレンス

sync.Mutex — 相互排他ロック(第2章)
方法: Lock(), Unlock(), TryLock() (Go 1.18+)
实现: state (int32) + sema (uint32)
模式: 正常模式 + 饥饿模式(等待超过 1ms 触发)

主な知識:

  • ゼロ値のまま使用でき、初期化は不要です
  • ロックを保持している側が解放します。保持していない状態で解放すると panic が発生します
  • 再入可能ではありません(再帰ロックではありません)
  • コピーできません(go vet で検出できます)
  • defer unlock はベストプラクティスです(Go 1.14+ では defer のパフォーマンスが最適化されています)
  • TryLock の使用場面は限られており、Go team は推奨していません
  • Go 1.26+ のランタイムは goroutine リークの検出をサポートしています

よくある落とし穴:

  • Unlock を忘れる(特に分岐内)
  • ロック取得の順序が一貫せず、デッドロックが発生する
  • ロックの再入によって自己デッドロックが発生する
  • Mutex インスタンスをコピーする
sync.RWMutex — 読み書きロック(第3章)
方法: Lock(), Unlock(), RLock(), RUnlock(), TryLock(), TryRLock()
场景: 读多写少(读写比越大收益越高)

主な知識:

  • 書き込みロックは読み取りロックより優先され、書き込み側の飢餓を防ぎます
  • TryLock/TryRLock はどちらも Go 1.18 で追加されました
  • 読み取り操作が 90% 以上を占める状況に適しています
sync.WaitGroup — タスクのオーケストレーション(第4章)
方法: Add(delta), Done(), Wait(), Go(f) (Go 1.25+)
实现: state (atomic.Uint64) + sema (uint32)

主な知識:

  • Add は Wait より前に呼び出す必要があります(goroutine の内部に置かないでください)
  • Done は実質的に Add(-1) です
  • カウンターを負の値にすることはできません
  • Go(1.25): wg.Go(f) は wg.Add(1); go func() { defer wg.Done(); f() }() と同等です
  • コピーできず、再利用もできません(Wait が返る前に再度 Add することはできません)

よくある落とし穴:

  • Add を goroutine の内部に置く(競合状態)
  • Add の回数を超えて Done を呼び出す(panic)
  • Wait が返る前に再度 Add する(panic)
  • Add を呼ばずに goroutine を起動する
sync.Cond — 条件変数(第5章)
方法: NewCond(l Locker), Wait(), Signal(), Broadcast()
场景: 等待某个条件满足后继续执行

主な知識:

  • Wait の前にロックを保持する必要があります。Wait は内部でロックを解放して待機します
  • 起床後に条件を再確認する必要があります(if ではなく for ループを使います)
  • Signal は 1 つを起こし、Broadcast はすべてを起こします
  • Signal/Broadcast の呼び出し時にロックを保持する必要はありません
sync.Once — シングルトンの初期化(第6章)
方法: Do(f func())
Go 1.21+: OnceFunc, OnceValue[T], OnceValues[T1,T2]

主な知識:

  • Do は、並行して呼び出された場合でも f が 1 回だけ実行されることを保証します
  • f が panic を起こした後、Once は実行済みとみなし、再試行しません
  • Reset はサポートされていません
  • OnceFunc: 1 回だけ実行される関数を返します
  • OnceValue[T]: 1 回だけ実行され、T を返す関数を返します
  • OnceValues[T1,T2]: 1 回だけ実行され、2 つの値を返す関数を返します
sync.Map — 並行処理対応 Map(第7章)
Go 1.24+: 实现重写为 hash-trie map
Go 1.23: Clear()
Go 1.24: CompareAndSwap(key, old, new), CompareAndDelete(key, old)

適した状況:

  • key は一度だけ書き込まれ、何度も読み取られる(キャッシュシステム)
  • 複数の goroutine が互いに重ならない key の集合を操作する
  • 不適:書き込みが多い場合、Len() が必要な場合

実装の仕組み:

  • read(読み取り専用、ロックなし)+ dirty(書き込み可能、ロックあり)
  • miss の回数がしきい値に達すると、dirty が read に昇格します
  • 遅延削除:先にマークし、後でクリーンアップします
  • Go 1.24 の hash-trie 実装では、重ならない key の変更時に競合が少なくなります
sync.Pool — オブジェクトプール(第8章)
方法: Get(), Put(x)
GC 行为: 每次 GC 清空 victim,local 降级为 victim

主な知識:

  • 取得するオブジェクトの型は一定ではないため、型アサーションが必要です
  • オブジェクトは GC 時に回収される可能性があります
  • Pool 内のオブジェクトについて、いかなる前提も置かないでください
  • 適用対象:頻繁に作成・破棄される一時オブジェクト
sync/atomic — アトミック操作(第10章)
Go 1.19: 类型安全原子类型 (Int32, Int64, Uint32, Uint64, Pointer, Bool)
Go 1.23: And(), Or() 位运算

主な知識:

  • 単純な状態の並行保護(flag、カウンター)に適しています
  • Mutex より軽量ですが、保証されるのは個々の操作の原子性だけです
  • 型安全なバージョンの使用を推奨します(例:atomic.AddInt64 の代わりに atomic.Int64 を使用)
Channel — 並行処理のオーケストレーション(第11-16章)

基本原則:

  • 「Don't communicate by sharing memory; share memory by communicating」
  • バッファなし channel は同期を保証します
  • バッファあり channel は非同期の疎結合を提供します
  • channel を閉じるのは送信側で、受信側は閉じられたことを検出します
  • select + default:ノンブロッキング操作
Context — コンテキスト制御(第9章)
Go 1.20: WithCancelCause
Go 1.21: WithTimeoutCause, WithDeadlineCause, AfterFunc

主な知識:

  • goroutine のライフサイクル管理とタイムアウト制御に使用します
  • Context を struct に保存しないでください
  • Context は最初の引数です
  • タイムアウト制御には time.After ではなく ctx.Done() を使います
  • Go 1.21+ の AfterFunc は、キャンセル後にクリーンアップを自動実行できます
testing/synctest — 並行処理テスト(Go 1.25+)
synctest.Test, synctest.Wait, synctest.Sleep (Go 1.27)
用途: 在隔离的时间环境中测试并发代码

判断マトリックス

选什么同步原语?
场景推荐理由
保护共享变量,读写均衡Mutex最简单可靠
保护共享变量,读多写少RWMutex读操作可并发
等待一组 goroutine 完成WaitGroup专为此场景设计
等待某个条件满足Cond / ChannelCond 更底层灵活
单例初始化(无返回值)sync.Once / OnceFuncGo 1.21+ 用 OnceFunc
单例初始化(有返回值)OnceValue[T]Go 1.21+,替代 Once+闭合
并发 map(key 固定,多读少写)sync.Map特殊场景,需 benchmark
并发 map(通用场景)map + RWMutex 或分片锁更通用的线程安全 map
并发 map(高并发写)分片 map (concurrent-map)减少锁竞争
简单 flag / 计数器atomic比 Mutex 轻量
对象池 / 缓存池sync.PoolGC 友好
goroutine 间通信ChannelGo 推荐的通信方式
生命周期控制 / 超时Context标准的取消传播机制
Channel vs Mutex?
对比维度ChannelMutex
数据所有权传递适合不适合
共享状态保护不适合适合
并发编排(等待/通知)适合需要配合 Cond
性能有内存拷贝开销无额外内存开销
易用性有并发语义保证需手动管理 Lock/Unlock
死锁风险有(channel 阻塞)有(锁顺序问题)

官方扩展并发原语

Semaphore — 信号量(第14章)

包: golang.org/x/sync/semaphore

方法: NewWeighted(n), Acquire(ctx, n), Release(n), TryAcquire(n)
实现: Mutex + List (waiter 链表, ready channel 通知)
场景: 控制多个 goroutine 同时访问多个资源

关键知识:

  • 可一次请求/释放多个资源(Weighted 的含义)
  • Acquire 支持 Context 取消
  • notifyWaiters 遇到第一个不满足的 waiter 就停止(防止饥饿)
  • Release 多于 Acquire → panic;请求 > 最大资源数 → 永久阻塞
SingleFlight — 合并请求(第15章)

包: golang.org/x/sync/singleflight

方法: Do(key, fn), DoChan(key, fn), Forget(key)
实现: Mutex + Map[string]*call, call 内含 WaitGroup
场景: 缓存击穿防护、合并并发读请求

与 sync.Once 的区别: Once 保证永远只执行一次;SingleFlight 每次调用重新执行,只合并同时的请求。

ErrGroup — 分组操作(第17章)

包: golang.org/x/sync/errgroup

方法: WithContext(ctx), Go(f), TryGo(f), SetLimit(n), Wait()
内部: WaitGroup + 信号量(channel) + Once + Context
场景: 一组 goroutine 任一失败则全部取消

关键知识:

  • 第一个非 nil error 取消 Context,Wait 返回该 error
  • SetLimit 控制并发 goroutine 数量
  • 零值也合法(但没有 Context 取消能力)
  • 需要收集所有结果时,用额外 slice 存储
Rate Limiter — 限流(第18章)

包: golang.org/x/time/rate

方法: NewLimiter(r, b), Allow/Reserve/Wait (及 N 版本)
类型: 令牌桶,容量 b (初始满桶),速率 r

令牌桶 vs 漏桶: 令牌桶允许突发(burst),漏桶严格平滑输出。对资源利用率要求高→令牌桶,对处理速度要求严格→漏桶。


常用第三方并发库

库类型亮点
marusama/cyclicbarrier循环屏障可重用,参与者互相等待,多轮使用
go-pkgz/syncs分组操作SizedGroup/ErrSizedGroup,控制并发数
vardius/gollback分组操作All/Race/Retry,直接返回结果和错误
AaronJan/Hunch分组操作All/Take/Last/Waterfall 多种编排
mdlayher/schedgroup定时任务组heap 排序避免大量 timer
juju/ratelimit令牌桶quantum 每次生成多个令牌
uber-go/ratelimit漏桶极简 API (Take),WithSlack 支持突发
go-redis/redis_rate分布式限流Redis + Lua, PerSecond/PerMinute
sony/gobreaker断路器Closed/Open/Half-Open 三态
sourcegraph/conc结构化并发panic 保护,更简洁的 WaitGroup 封装
panjf2000/antsWorker Pool高性能 goroutine 池
cespare/percpuPer-CPU无锁高性能计数器
valyala/bytebufferpoolBuffer Poolcalibrate 智能调整池大小
cenk/backoff重试指数退避

并发模式速查(第20章)

模式场景Go 中的体现
Half-Async/Half-Sync网络服务Go 网络库、RPC Client
Active Object方法异步执行channel 解耦调用和执行
Circuit Breaker故障保护sony/gobreaker
Deadline/Timeout超时控制Context + Timer
Balking防重复执行atomic CAS 检查
Double-Checked Locking延迟初始化sync.Once 的实现
Guarded Suspension条件等待Cond/Channel 保护式挂起
Nuclear Reaction数据合并/分解并发快排、多级爬虫
Scheduler任务调度GMP 调度器
ReactorI/O 事件处理Go net 包
Proactor异步 I/Oxtaci/gaio
Per-CPU无锁高性能sync.Pool, Timer

分布式同步原语(第19章)

基于 etcd 实现 (go.etcd.io/etcd/client/v3):

原语说明
ElectionLeader 选举 (Campaign/Proclaim/Resign/Observe)
Locker简单分布式锁 (sync.Locker 接口)
Mutex带 TTL 分布式锁 (崩溃后自动释放)
RWMutex分布式读写锁
Queue/PriorityQueue多读多写分布式队列
Barrier/DoubleBarrier分布式屏障(一次性/两阶段)
STM软件事务内存 (CAS 原子操作)

关键知识:

  • 持有锁的节点崩溃后,Mutex 在 TTL(默认 60s)后自动释放
  • DoubleBarrier: Enter 等 count 个节点进入,Leave 等 count 个节点离开
  • 优先级队列:数值越小越优先出队

经典并发问题(第21章)

问题核心挑战解法
哲学家就餐死锁的四个必要条件限制人数/奇偶编号/资源分级/服务生
理发师问题并发队列(多写单读)Cond 或 Channel Semaphore
水工厂问题三线程协同+循环CyclicBarrier + Semaphore
Fizz Buzz四 goroutine 交替输出channel 链串行化

参考資料

より詳細な情報が必要な場合は、以下を読み込んでください。

  • references/traps.md — 各 sync プリミティブでよくある落とし穴と誤りのパターン
  • references/patterns.md — 並行処理の設計パターンとベストプラクティス
  • references/version-changes.md — Go 1.20→1.27 における sync パッケージの変更点の詳細
  • references/deadlock.md — デッドロックの診断と goroutine リークの検出
  • references/memory-model.md — Go メモリモデルの要点
  • references/extended-primitives.md — 公式の拡張並行処理プリミティブ(セマフォ/SingleFlight/ErrGroup/レート制限)
  • references/third-party-libs.md — サードパーティの並行処理ライブラリ(CyclicBarrier/gollback/Hunch/サーキットブレーカー/WorkerPool など)
  • references/concurrency-patterns.md — 並行処理パターン(Half-Async/Sync、Active Object、Reactor など)
  • references/distributed-primitives.md — 分散同期プリミティブ(etcd ベースの選挙/ロック/キュー/バリア/STM)
  • references/classic-problems.md — 古典的な並行処理の問題(哲学者/理髪師/水工場/FizzBuzz)

クイックチェックリスト

Go の並行処理コードをレビューする際は、次の項目を確認してください。

  • -race を使ってテストしましたか?
  • go vet を使って検査しましたか(ロックのコピー検出)?
  • ロックの保持時間は可能な限り短くなっていますか?
  • ロックの再入が発生する可能性はありますか?
  • すべての経路で Unlock が呼び出されていますか?(panic/error の経路も含む)
  • ロックの取得順序は一貫していますか?
  • WaitGroup.Add は Wait より前に呼び出されていますか?
  • WaitGroup.Done は正しく対になっていますか?
  • Once.Do の f が panic を起こし、初期化されないリスクはありますか?
  • goroutine 内で t.Fatal を使用していませんか?
  • map の並行読み書きは保護されていますか?
  • sync プリミティブをコピーしていませんか?
  • Context は第 1 引数になっていますか?
  • Channel を閉じる側は正しいですか?(送信側が閉じる)

この Skill は《Go 並行プログラミング実践》に基づいて作成されており、Go 1.20 ~ 1.27 の sync 関連知識を網羅しています。

コメント