Channel 与并发模式
Channel 是 goroutine 之间通信的管道,类型化、线程安全。
基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ch := make(chan int) ch := make(chan int, 10)
ch <- 42 v := <-ch
close(ch)
v, ok := <-ch if !ok { }
|
无缓冲 vs 有缓冲
| 类型 |
行为 |
| 无缓冲 |
发送方阻塞直到接收方就绪(同步握手) |
| 有缓冲 |
缓冲区未满时发送不阻塞,满则阻塞 |
1 2 3 4
| ch := make(chan int, 2) ch <- 1 ch <- 2
|
range 遍历 channel
1 2 3 4 5 6 7 8 9 10 11 12
| func producer(ch chan<- int) { for i := 0; i < 5; i++ { ch <- i } close(ch) }
func consumer(ch <-chan int) { for v := range ch { fmt.Println(v) } }
|
单向 channel
限制 channel 只能发送或接收,编译期约束:
1 2 3 4 5 6 7 8
| func sendOnly(ch chan<- int) { ch <- 1 }
func receiveOnly(ch <-chan int) { v := <-ch _ = v }
|
select 多路复用
同时等待多个 channel 操作:
1 2 3 4 5 6 7 8 9 10
| select { case msg := <-ch1: fmt.Println("from ch1:", msg) case msg := <-ch2: fmt.Println("from ch2:", msg) case <-time.After(time.Second): fmt.Println("timeout") default: fmt.Println("no channel ready") }
|
常见并发模式
Worker Pool
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { results <- j * 2 } }
func main() { jobs := make(chan int, 100) results := make(chan int, 100)
for w := 1; w <= 3; w++ { go worker(w, jobs, results) }
for j := 1; j <= 5; j++ { jobs <- j } close(jobs)
for r := 1; r <= 5; r++ { fmt.Println(<-results) } }
|
Pipeline
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| func gen(nums ...int) <-chan int { out := make(chan int) go func() { for _, n := range nums { out <- n } close(out) }() return out }
func square(in <-chan int) <-chan int { out := make(chan int) go func() { for n := range in { out <- n * n } close(out) }() return out }
for n := range square(gen(1, 2, 3, 4)) { fmt.Println(n) }
|
Fan-out / Fan-in
多个 goroutine 消费同一 channel(fan-out),多个 channel 合并到一个(fan-in),适合并行处理与结果聚合。
context 取消
配合 context 实现超时与取消(详见 context 使用):
1 2 3 4 5 6 7 8 9
| ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel()
select { case <-ctx.Done(): return ctx.Err() case result := <-resultCh: return result }
|
小结
- channel 用于 goroutine 间安全通信,遵循”谁发送谁关闭”原则
select 处理多 channel 场景
- Worker Pool、Pipeline 是生产环境常见模式
- 避免向已关闭的 channel 发送数据(会 panic)