使用 chan struct{} 是因为零内存开销且语义明确:struct{} 占 0 字节,而 bool 占 1 字节、int 占 8 字节;数百 goroutine 下可节省数十 KB 内存;其零值唯一、地址相同,支持安全比较;编译器专门优化,发送/接收无额外开销;见名知意,表明仅作信号传递。

为什么用 chan struct{} 而不是 chan bool 或 chan int
因为零内存开销 + 语义明确。一个 struct{} 实例占 0 字节,而 bool 占 1 字节、int 通常占 8 字节(64 位系统)。当你启动几百个 goroutine,每个都配一个退出通道,累积下来,chan bool 可能多占几十 KB 内存——这在微服务或高并发网关里就是实打实的浪费。
-
struct{}的零值是唯一的,且所有struct{}{}实例地址相同(可安全比较) - 编译器对
chan struct{}有专门优化,发送/接收几乎无额外开销 - 看到
chan struct{}就知道:这是纯信号,不传数据——别人读你代码时一眼就懂意图
done 必须带 <code>{},不能只写 done
这是初学者最常卡住的地方:struct{} 是类型,struct{}{} 才是它的零值字面量。就像你不能写 make([]int),得写 make([]int, 0);同理,向通道发信号必须提供一个具体值,所以必须加 {}。
- 错写:
done → 编译错误:<code>syntax error: unexpected } - 对写:
done → 正确,发送一个空结构体零值 - 注意:不能省略括号,
struct{}{}是固定写法,不是“可选”
关闭 chan struct{} 后还能读,但要小心 select 行为
关闭一个 chan struct{} 后,任何 都会立即返回 <code>struct{}{}(零值),且 ok == false。这使得它特别适合做“一次性完成通知”。
- 典型用法:
close(done)表示任务结束,接收方立即解阻塞 - 陷阱:如果用
select监听已关闭的chan struct{},且其他分支也 ready,调度不保证选哪个——别依赖顺序 - 更稳妥的写法是直接
(无 <code>select),或配合default做非阻塞检查:select { case
别 close 多次,也别往已关闭的 chan struct{} 发送
Go 对通道关闭非常严格:重复 close(ch) 会 panic;往已关闭的 ch 发送任何值(包括 struct{}{})也会 panic。而接收是安全的——这点和 chan int 一样,但新手容易忽略。
立即学习“go语言免费学习笔记(深入)”;
- panic 示例:
close(done); close(done)→panic: close of closed channel - panic 示例:
close(done); done → <code>panic: send on closed channel - 安全做法:只由**单一方**负责关闭(通常是启动 goroutine 的那一方),或用
sync.Once包一层 - 接收方永远不用管关没关,
永远安全,只是关了之后立刻返回
真正难的不是怎么写 chan struct{},而是判断谁该关、什么时候关、关完要不要再用——这些得结合业务生命周期来设计,而不是套模板。










