channel 基础
本教程共 80 篇 · 第 56 篇 · 更新于 2026-07-27 · 约 7 分钟阅读
56. channel 基础
本节目标:学会创建 channel、发送和接收数据,理解阻塞机制,掌握无缓冲 channel 的同步用法。
channel 是什么
channel(通道)是 goroutine 之间传数据的管道。Go 有句名言:
不要通过共享内存来通信,而要通过通信来共享内存。
channel 就是这句话的落地工具。你可以把它想成一根管子,一头塞数据进去,另一头取出来。
创建 channel
用 make 创建,类型是 chan 元素类型:
ch := make(chan int) // 无缓冲 channel
ch2 := make(chan string) // string 类型的 channel
声明的 channel 变量如果没初始化,值是 nil:
var ch chan int
fmt.Println(ch == nil) // true
对 nil channel 发送或接收会永远阻塞,所以用之前一定要 make。
发送和接收
用 <- 操作符,箭头方向就是数据流向:
ch <- 10 // 把 10 发送到 ch
v := <-ch // 从 ch 接收,赋值给 v
一个完整的例子:
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
ch <- 42 // 发送
}()
v := <-ch // 接收
fmt.Println(v) // 42
}
主 goroutine 在 v := <-ch 处阻塞,等子 goroutine 把数据塞进来才继续。
阻塞行为
这是 channel 最核心的特性:发送和接收默认都会阻塞。
- 发送方:没人接收就一直等
- 接收方:没数据来就一直等
这不是 bug,是设计如此。正是这种阻塞让 goroutine 能同步,不用加锁。
无缓冲 channel 的同步
make(chan int) 创建的是无缓冲 channel。它没有暂存空间,发送方必须等到接收方准备好,两个 goroutine 才能「握手」成功。
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
go func() {
fmt.Println("准备发送...")
ch <- 1
fmt.Println("发送完成")
}()
time.Sleep(time.Second)
fmt.Println("准备接收")
fmt.Println("收到:", <-ch)
}
输出大致如下(最后两行顺序可能互换):
准备发送...
准备接收
收到: 1
发送完成
发送方卡在 ch <- 1,直到主 goroutine 来接收才放行。这就是同步效果。
同步使用会死锁
无缓冲 channel 不能在同一个 goroutine 里又发又收,否则死锁:
func main() {
ch := make(chan int)
ch <- 1 // 阻塞!没人接收
fmt.Println(<-ch) // 永远执行不到
}
运行会报错:fatal error: all goroutines are asleep - deadlock!
Warning无缓冲 channel 的发送和接收必须分属不同的 goroutine。同一 goroutine 内同步收发必然死锁。
接收的两个返回值
接收操作可以拿到第二个返回值,表示是否成功读到数据:
v, ok := <-ch
ok 为 false 表示 channel 已关闭且没有数据了。这个后面讲 close 时会用到。
关闭 channel
用 close(ch) 关闭一个 channel。关闭后:
- 不能再发送(会 panic)
- 还能继续接收已缓冲的数据
- 数据收完后,接收返回零值和
false
ch := make(chan int, 2)
ch <- 1
ch <- 2
close(ch)
fmt.Println(<-ch) // 1
fmt.Println(<-ch) // 2
v, ok := <-ch // 0, false
Notechannel 不是文件,通常不需要手动关闭。只有当你需要通知接收方「没有更多数据了」时才关闭,比如配合
range遍历。关闭细节下一节细讲。
channel 是引用类型
channel 传给函数时,传的是同一个引用:
func send(ch chan int) {
ch <- 100
}
func main() {
ch := make(chan int)
go send(ch)
fmt.Println(<-ch) // 100
}
不需要传指针,函数内部操作的 channel 和外面是同一个。
小结
make(chan T)创建无缓冲 channelch <- v发送,<-ch接收,默认阻塞- 无缓冲 channel 强制收发双方同步
- 同一 goroutine 内对无缓冲 channel 收发会死锁
- 接收有第二个返回值
ok,判断 channel 是否关闭
下一节讲缓冲 channel 和单向 channel。