mutex.lock()后不加defer mutex.unlock()易致死锁,因函数中途return或panic时锁未释放,其他goroutine将永久阻塞;channel关闭后继续send必panic,仅sender可关闭,多sender需统一协调。

为什么 mutex.Lock() 后不加 defer mutex.Unlock() 会死锁
不是所有加锁都要配 defer 解锁,但绝大多数场景下漏掉它,程序会在某个 goroutine 里卡死不动——因为其他 goroutine 在等这把永远不释放的锁。
典型触发场景:函数中途 return、panic、或者有多个 return 路径。比如错误处理分支直接 return,却忘了 unlock。
- 必须在
Lock()后紧跟着写defer Unlock(),哪怕后面还有几十行代码 - 如果逻辑需要提前释放锁(比如先查缓存命中就不用继续持锁),那就别用 defer,改用手动
Unlock(),但务必确保每条路径都覆盖 - 用
go vet可以捕获部分未配对的锁操作,但它不保证 100% 发现;更可靠的是用sync.Mutex的竞态检测器:go run -race main.go
Channel 关闭后继续 send 会 panic:send on closed channel
这个 panic 不是“可能出错”,而是只要发生就必然崩溃。和读取关闭的 channel(返回零值)不同,写入是明确禁止的。
常见于生产者-消费者模型中:生产者关 channel 后,消费者还在往里面塞数据;或者多个 goroutine 都以为自己该关 channel,结果重复 close。
- 只有 sender 有权关闭 channel,receiver 绝对不能 close
- 多个 sender 时,必须由一个协调者(比如主 goroutine)统一关闭,不能各自判断完就关
- 不确定是否已关?用
select+default做非阻塞 send,或用len(ch) 粗略判断缓冲区是否还有空间(仅限带缓冲 channel) - 关闭前可加一层检查:
if ch != nil { close(ch) },避免重复 close 导致 panic
Context.WithCancel() 返回的 cancel 函数不调用会泄漏 goroutine
cancel 函数不只是“通知停止”,它还会清理内部的 goroutine 和 timer。不调用,context 树就一直挂着,相关 goroutine 永远不会退出。
最典型的是 HTTP handler 中启了子 goroutine 做异步任务,但 handler 结束时没调用 cancel,导致子 goroutine 一直跑着,还拿着 request 的引用(可能含 body reader),内存和连接都卡住。
- 每个
WithCancel/WithTimeout/WithDeadline都要配对调用 cancel,且只能调用一次 - 在 defer 中调用最安全:
defer cancel(),但注意别在循环里 defer(会累积) - 如果 cancel 函数传给了其他包或回调,得明确文档约定“调用方负责 cancel”,否则极易遗漏
Reactor 模式里用 Flux.create() 而不是 Flux.fromIterable() 的真实原因
fromIterable() 是同步拉取全部数据再发出去,整个过程阻塞当前线程;而 create() 允许你控制何时、以何种节奏 emit,真正实现背压和异步源头接入。
比如读文件、调第三方 API、监听消息队列——这些都不是“一口气给全”的操作,硬套 fromIterable() 会导致响应延迟、OOM 或失去对下游流控的响应能力。
-
create()的 lambda 参数是FluxSink,它提供next()/error()/complete(),且支持request()回调来响应下游需求 - 若源头本身是阻塞 IO,记得用
publishOn()切换到 io 调度器,否则会堵住整个 reactor 线程池 - 别在
create()里做耗时计算或无限循环,它运行在 reactor 线程上,会影响整个 pipeline 吞吐










