在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。
本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。
一、Channel 简介
Channel 是 Go 语言中实现 "通信顺序进程"(CSP)模型的关键组件。
- • 用于在多个 Goroutine 之间传递数据
- • 本质上是一个 先进先出 的队列
- • 保证并发安全
通道类型的声明格式:
go
var ch chan 数据类型
二、Channel 的创建与基本使用
go
ch := make(chan int) // 创建一个传输 int 类型的无缓冲通道
发送与接收数据:
go
ch <- 10 // 发送数据
x := <- ch // 接收数据
注意:无缓冲通道发送和接收都是阻塞的,直到对方准备好。
示例:
go
func worker(ch chan string) {
msg := <-ch
fmt.Println("接收到:", msg)
}
func main() {
ch := make(chan string)
go worker(ch)
ch <- "Hello, Channel"
}
三、带缓冲的 Channel
go
ch := make(chan int, 3) // 创建一个缓冲区大小为3的通道
特点:
- • 发送操作在缓冲区满时阻塞
- • 接收操作在缓冲区空时阻塞
r
ch <- 1
ch <- 2
fmt.Println(<-ch) // 输出 1
四、通道的关闭
scss
close(ch)
- • 关闭通道后不能再发送数据,但仍可接收剩余数据
- • 读取已关闭通道不会阻塞,返回类型零值
- • 可以用
v, ok := <-ch
判断通道是否关闭
示例:
go
ch := make(chan int, 2)
ch <- 10
ch <- 20
close(ch)
for v := range ch {
fmt.Println(v)
}
五、单向通道
可以将通道限制为只发送 或只接收:
go
func send(ch chan<- int) {
ch <- 100
}
func recv(ch <-chan int) {
fmt.Println(<-ch)
}
六、使用 select 同时监听多个 Channel
go
select {
case msg1 := <-ch1:
fmt.Println("ch1:", msg1)
case msg2 := <-ch2:
fmt.Println("ch2:", msg2)
default:
fmt.Println("无数据可读")
}
特点:
- •
select
会等待多个通道中的一个准备好 - • 如果多个都准备好了,则随机选择一个执行
- • 使用
default
实现非阻塞操作
七、超时控制
结合 time.After()
实现超时机制:
css
select {
case data := <-ch:
fmt.Println("收到数据:", data)
case <-time.After(2 * time.Second):
fmt.Println("超时未收到数据")
}
八、通道的常见应用场景
场景 | 描述 |
---|---|
Goroutine 通信 | 不共享内存,通过通道交换数据 |
控制并发数量(限流) | 使用带缓冲通道实现并发任务限制 |
Worker Pool(协程池) | 多个 worker 从任务通道中取任务处理 |
信号通知/任务完成通知 | 通道发送空 struct{} 信号实现同步或退出控制 |
超时控制 / 取消上下文 | select + time.After / context 控制并发 |
九、小结
- • Channel 是 Go 并发通信的核心工具
- • 区分无缓冲/有缓冲通道,理解其阻塞机制
- • 使用
close
安全关闭通道 - •
select
可实现多路复用、超时控制等高级场景