← ClaudeAtlas

concurrency-safetylisted

写并发/异步代码时使用。防止竞态、死锁、资源泄漏。
Wade-DevCode/awesome-coding-skills-cn · ★ 3 · AI & Automation · score 78
Install: claude install-skill Wade-DevCode/awesome-coding-skills-cn
# 并发安全 ## 何时用 - 写多线程、多协程、多进程代码,共享状态需要同步时。 - 异步任务(`asyncio`/`goroutine`/`CompletableFuture`/`Task`)有超时、取消或错误传播需求时。 - 发现压测下出现数据不一致、随机 panic、连接池耗尽等难以稳定复现的 bug 时。 - code review 发现锁的获取顺序不一致,或资源在异常路径下未释放时。 ## 核心规则 ### 1. 共享可变状态必须加保护,识别 check-then-act 竞态 **规则:** 所有被多个线程/协程读写的可变状态,必须用锁、原子操作或不可变数据结构保护;尤其要识别"先检查再操作"(check-then-act)这一最常见的竞态模式——检查和操作之间的窗口期可能被其他线程插入。 **为什么:** AI 生成并发代码时最常见的错误是:检查 `if count < limit` 后再递增——看起来没问题,但在高并发下两个线程可能同时通过检查,两个都执行递增,导致超出限制。还有"懒初始化"竞态:`if instance is None: instance = ...`,两个线程可能同时判断为 None 并各自创建一个实例。这类 bug 在低并发下压根不触发,在生产流量高峰时突然出现,且极难稳定复现。 **怎么做:** ```go // 反例:check-then-act 竞态(Go) if counter < limit { // ❌ 检查 counter++ // ❌ 操作:两步之间有窗口,并发时超限 } // 正例:用原子操作或锁合并 check 和 act mu.Lock() if counter < limit { counter++ mu.Unlock() proceed() } else { mu.Unlock() return ErrLimitExceeded } // 或者对于简单计数器,使用 sync/atomic newVal := atomic.AddInt64(&counter, 1) if newVal > limit { atomic.AddInt64(&counter, -1) // 回退 return ErrLimitExceeded } ``` - 共享变量的读写要么全部在锁内,要么全部用原子类型(`sync/atomic`、`std::atomic`、`Interlocked`)。 - 不可变对象天然线程安全,优先设计成不可变:初始化后不修改,需要"修改"时创建新对象。 - 使用通道(channel)/消息传递代替共享状态时,明确通道的所有权(谁关闭、谁读、谁写)。 --- ### 2. 锁粒度小、获取顺序一致,能用无锁结构优先 **规则:** 锁的粒度要尽量细(只锁需要保护的最小代码段),多个锁的获取顺序在整个代码库中必须一致;有线程安全的数据结构(`sync.Map`、`ConcurrentHashMap`、`channel`)可以替代手动锁时优先选用。 **为什么:** AI 生成代码时有两种相反的极端:一种是粗粒度地用一把大锁保护整个方法,锁持有时间过长,并发度接近零;另一种是在不同地方以不同顺序加多把锁——函数 A 先锁 X 再锁 Y,函数 B 先锁 Y 再锁 X,只要这两��函数同时在不同线程执行,就必然死锁。死锁在开发环境极难触发,因为并发度不够高