关闭channel后读取返回零值且ok为false,不会panic;for range最安全,手动读取需检查ok以避免误将零值当有效数据。

channel关闭后读取会返回零值,不是panic
很多人误以为关闭 channel 后再读会 panic,其实不会。它会立即返回对应类型的零值,且 ok 为 false。这容易导致逻辑错判——比如把关闭后的 0 当成真实数据处理。
- 用
for range ch最安全:自动在 channel 关闭时退出循环 - 手动读取必须检查
ok:val, ok := ,仅当 <code>ok == true才处理val - 关闭已关闭的 channel 会 panic,所以只由 sender 关闭,receiver 不要关
- 多个 goroutine 同时向一个 channel 发送时,需协调关闭时机,常见做法是用
sync.WaitGroup+close()配合
slice 和 array 的行为差异直接影响内存和并发安全
数组是值类型,赋值或传参时整块拷贝;slice 是引用类型,底层共享同一段底层数组。这点在函数传参、goroutine 间传递、切片扩容时特别关键。
- 修改一个 slice 的元素可能意外影响另一个(如果它们共用底层数组),例如
s1 := s[0:3]和s2 := s[2:5]可能重叠 -
append可能触发底层数组扩容,导致原 slice 和新 slice 脱离共享——但无法提前预知,不能依赖“是否还共享”做逻辑判断 - 需要隔离数据时,显式复制:
newSlice := append([]int(nil), oldSlice...)或用copy - 声明空 slice 优先用
var s []int(零值安全),而非make([]int, 0)(虽等价,但前者更明确语义)
变量/包未使用会导致编译失败,不是警告
Go 编译器强制要求所有声明的变量、导入的包都必须被实际使用,否则直接报错。这不是风格问题,而是语言设计决定的。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
-
import "fmt"却没调用任何fmt函数?编译失败。临时调试可加下划线导入:import _ "fmt"(仅执行 init) - 声明了
err却只在if err != nil里用了一次,后面没再用?仍算“已使用”;但如果整个 if 块被注释掉,err就变成未使用 - 想忽略某个返回值,必须用下划线:
_, ok := m[key],不能只写m[key](此时 key 会被计算两次,且返回值被丢弃但不算“使用”) - 单元测试文件(
*_test.go)中,测试函数外声明的变量不受此限制,但包级变量仍需使用
sync.Cond 的 Wait 必须在锁保护下调用
sync.Cond 不是独立锁,它依赖外部 sync.Mutex 或 sync.RWMutex。漏掉锁或顺序错乱,会导致竞态或死锁。
立即学习“go语言免费学习笔记(深入)”;
- 正确流程:先
mu.Lock()→ 检查条件是否满足 → 不满足则cond.Wait()(它会自动释放锁并休眠)→ 唤醒后重新持有锁 → 再次检查条件 -
cond.Wait()返回时,锁一定已被重新获取,所以唤醒后的条件检查必须在锁内完成 -
Signal()和Broadcast()可在锁外调用,但通常建议在锁内调用以保证条件更新与通知的原子性 - 别用
time.Sleep替代cond.Wait:既浪费 CPU,又无法响应条件变化
最常被忽略的是“条件检查必须循环进行”——因为 Wait 可能被虚假唤醒,也可能是多 goroutine 竞争导致条件在唤醒后又被改回不满足状态。写法上永远用 for !condition { cond.Wait() },而不是 if !condition { cond.Wait() }。

















