1. channel 基本概念

Go 的 channel(通道)是 goroutine 之间通信的核心机制,支持安全地在多个协程间传递数据。

  • channel 定义channel 是一种类型安全的队列,支持发送(send)和接收(receive)操作。
  • 声明方式make(chan T) 创建一个元素类型为 T 的 channel。
  • 本质作用:实现 goroutine 之间的同步与数据共享。

2. channel 的类型与区别

2.1 无缓冲 channel

  • 创建方式:make(chan T)
  • 特点:
    • 发送和接收操作必须同步进行。
    • 发送方会阻塞,直到有接收方接收数据。
    • 适合严格同步、协作场景。

示例:

1
2
3
4
5
6
7
8
9
results := make(chan int)
for i := 0; i < 2; i++ {
go func(i int) {
results <- i // 阻塞,直到主协程接收
}(i)
}
for i := 0; i < 2; i++ {
<-results
}

2.2 有缓冲 channel

  • 创建方式:make(chan T, N),N 为缓冲区大小。
  • 特点:
    • 发送方只要缓冲区未满就不会阻塞。
    • 接收方只要缓冲区不为空就能立即取到数据。
    • 适合高并发、批量收集等场景。

示例:

1
2
3
4
5
6
7
8
9
results := make(chan int, 2)
for i := 0; i < 2; i++ {
go func(i int) {
results <- i // 只要缓冲区没满,不会阻塞
}(i)
}
for i := 0; i < 2; i++ {
<-results
}

3. 应用场景对比

3.1 无缓冲 channel 典型应用

  • 强同步:如生产者-消费者一对一、协程轮流执行、信号通知等。
  • 示例:严格同步
1
2
3
4
5
6
7
8
ch := make(chan int)
go func() {
val := <-ch
fmt.Println("收到数据:", val)
}()
fmt.Println("准备发送数据")
ch <- 42 // 阻塞,直到上面协程接收
fmt.Println("数据已发送")

3.2 有缓冲 channel 典型应用

  • 高并发批量收集:如并发任务结果收集、日志异步写入等。
  • 示例:批量收集
1
2
3
4
5
6
7
8
9
results := make(chan int, 100)
for i := 0; i < 100; i++ {
go func(i int) {
results <- i
}(i)
}
for i := 0; i < 100; i++ {
<-results
}

4. 性能对比实验(高并发场景)

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
27
28
29
30
31
32
33
34
35
package main
import (
"fmt"
"time"
)
func main() {
const N = 1000000
// 无缓冲 channel
results1 := make(chan int)
start := time.Now()
for i := 0; i < N; i++ {
go func(i int) {
results1 <- i
}(i)
}
time.Sleep(1 * time.Second)
for i := 0; i < N; i++ {
<-results1
}
fmt.Printf("无缓冲 channel 总耗时: %v\n", time.Since(start))

// 有缓冲 channel
results2 := make(chan int, N)
start = time.Now()
for i := 0; i < N; i++ {
go func(i int) {
results2 <- i
}(i)
}
time.Sleep(1 * time.Second)
for i := 0; i < N; i++ {
<-results2
}
fmt.Printf("有缓冲 channel 总耗时: %v\n", time.Since(start))
}

实验结果示例:

1
2
无缓冲 channel 总耗时: 2.8s
有缓冲 channel 总耗时: 1.4s
  • 结论:高并发下,有缓冲 channel 能显著减少阻塞、提升性能。

5. 场景举例与最佳实践

5.1 无缓冲 channel 场景

  • 线程/协程间轮流执行任务
  • 信号通知、流水线严格同步
  • 需要“你等我、我等你”的协作逻辑

5.2 有缓冲 channel 场景

  • 并发任务批量收集结果
  • 日志、消息异步处理
  • 缓冲区大小建议等于并发 goroutine 数量

6. 总结与建议

  • 无缓冲 channel:适合需要强同步、协作的场景。
  • 有缓冲 channel:适合高并发、批量处理,能显著提升性能。
  • 选择建议
    • 需要严格同步时用无缓冲 channel
    • 需要高并发/批量收集时用有缓冲 channel
    • 缓冲区大小建议根据实际并发量设置

合理选择 channel 类型,有助于提升 Go 程序的并发性能与可读性。