Go channel类型与通信详解
Channel实现goroutine间安全通信,遵循"不要通过共享内存通信,而是通过通信共享内存"。
Channel类型
无缓冲channel
Go
// 创建无缓冲channel
ch := make(chan int)
// 特性:
// - 发送必须等待接收
// - 接收必须等待发送
// - 同步语义强
有缓冲channel
Go
// 创建有缓冲channel
ch := make(chan int, 3)
// 特性:
// - 缓冲未满时可异步发送
// - 缓冲非空时可异步接收
// - 缓冲满时发送阻塞
// - 缓冲空时接收阻塞
| 类型 | 发送条件 | 接收条件 | 用途 |
|---|---|---|---|
| 无缓冲 | 有人接收 | 有人发送 | 同步 |
| 有缓冲 | 缓冲不满 | 缓冲不空 | 数据传递 |
Channel方向
双向channel
Go
// 默认双向
ch := make(chan int)
// 可发送也可接收
ch <- 1
v := <-ch
单向channel
Go
// 只发送channel
var sendOnly chan<- int = ch
// 只接收channel
var recvOnly <-chan int = ch
// 用于函数参数限制方向
func sender(ch chan<- int) {
ch <- 1 // 只能发送
}
func receiver(ch <-chan int) {
v := <-ch // 只能接收
}
Channel通信操作
发送数据
Go
ch := make(chan int, 2)
ch <- 1 // 发送成功(缓冲未满)
ch <- 2 // 发送成功
ch <- 3 // 阻塞(缓冲满,等待接收)
接收数据
Go
v := <-ch // 推入变量
fmt.Println(<-ch) // 直接使用
<-ch // 丢弃值
// 检查channel状态
v, ok := <-ch
if ok {
fmt.Println("收到:", v)
} else {
fmt.Println("channel已关闭")
}
遍历channel
Go
// range自动接收直到关闭
for v := range ch {
fmt.Println(v)
}
// channel关闭时自动退出
Channel关闭
关闭语法
Go
ch := make(chan int, 5)
ch <- 1
ch <- 2
close(ch) // 关闭channel
关闭后的行为
Go
// 发送:panic
ch <- 3 // panic: send on closed channel
// 接收:返回零值和false
v, ok := <-ch // v=0, ok=false
// 再次关闭:panic
close(ch) // panic: close of closed channel
关闭规则
Go
// 规则:
// 1. 只有发送者关闭
// 2. 不要关闭已关闭的channel
// 3. 接收者不要关闭
// 正确示例
func producer(ch chan<- int) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch) // 发送者关闭
}
func consumer(ch <-chan int) {
for v := range ch { // 自动处理关闭
fmt.Println(v)
}
}
nil channel行为
nil channel特性
Go
var ch chan int // nil channel
// 发送:永久阻塞
ch <- 1 // 永久阻塞
// 接收:永久阻塞
<-ch // 永久阻塞
// 关闭:panic
close(ch) // panic: close of nil channel
利用nil channel
Go
// 在select中禁用channel
var ch1, ch2 chan int
for {
select {
case v := <-ch1: // ch1=nil时阻塞,不执行
process1(v)
case v := <-ch2:
process2(v)
}
// 动态设置ch1/ch2为nil或有效channel
// 实现动态启用/禁用case
}
Channel容量选择
容量为0
Go
ch := make(chan int)
// 同步语义:发送和接收必须同时就绪
// 适用:同步信号、握手
容量为1
Go
ch := make(chan int, 1)
// 一次异步传递
// 适用:结果缓存、单次通知
容量大于1
Go
ch := make(chan int, 100)
// 批量数据传递
// 适用:worker pool、pipeline
通信语义对比
| 操作 | 无缓冲 | 有缓冲(未满) | 有缓冲(满) | 已关闭 |
|---|---|---|---|---|
| 发送 | 阻塞等待 | 立即成功 | 阻塞等待 | panic |
| 接收 | 阻塞等待 | 立即成功 | 立即成功 | 返回零值 |
常见用法示例
同步信号
Go
done := make(chan struct{})
go func() {
time.Sleep(1 * time.Second)
done <- struct{}{} // 通知完成
}()
<-done // 等待完成
数据传递
Go
ch := make(chan int, 10)
go func() {
for i := 0; i < 10; i++ {
ch <- i // 发送数据
}
close(ch)
}()
for v := range ch { // 接收数据
fmt.Println(v)
}
单向channel限制
Go
func pipeline(in <-chan int, out chan<- int) {
for v := range in {
out <- v * 2
}
}
要点总结
- 无缓冲channel同步强,发送等待接收
- 有缓冲channel异步传递,缓冲满才阻塞
- 双向channel可发可收,单向channel限制方向
- 只有发送者关闭channel
- 关闭后发送panic,接收返回零值+false
- nil channel在select中永久阻塞(可禁用case)
- range遍历自动处理关闭
- 用容量0同步,容量>0传递数据
📝 发现内容有误?点击此处直接编辑