channel

通道(channel)是 goroutine 之间传递值的类型安全管道,遵循先进先出顺序。发送方与接收方在通道上的操作天然同步,因此 channel 同时承担通信同步两种职责。

基本操作

package main

import "fmt"

func main() {
    ch := make(chan string) // 创建传递 string 的无缓冲通道

    go func() {
        ch <- "hello" // 发送
    }()

    msg := <-ch // 接收(阻塞直至有值)
    fmt.Println(msg)
}

通道操作一览:

操作语法说明
创建make(chan T) / make(chan T, n)无缓冲 / 容量为 n 的有缓冲
发送ch <- v无缓冲时阻塞到有接收者
接收v := <-ch阻塞到有发送者
关闭close(ch)通知接收方不再有数据
非阻塞探测v, ok := <-chok 为 false 表示通道已关闭且取空

无缓冲与有缓冲

package main

import "fmt"

func main() {
    // 无缓冲:发送与接收必须同时就绪(同步交接)
    sync := make(chan int)

    // 有缓冲:缓冲区未满时发送不阻塞(异步传递)
    buffered := make(chan int, 2)
    buffered <- 1
    buffered <- 2
    // buffered <- 3 // 若取消注释:缓冲已满,将永久阻塞

    fmt.Println(<-buffered, <-buffered, len(buffered), cap(buffered))

    go func() { sync <- 42 }()
    fmt.Println(<-sync)
}
Tip

选型经验:无缓冲通道强调"交接时刻"的同步语义,适合信号与任务分发;有缓冲通道用于平滑突发流量或解耦生产/消费速率,容量应基于明确的背压策略而非"越大越好"。

关闭与遍历

发送方负责关闭通道;向已关闭的通道发送数据会 panic:

package main

import "fmt"

func produce(nums chan<- int) {
    for i := 1; i <= 3; i++ {
        nums <- i
    }
    close(nums) // 发送完毕,关闭通道
}

func main() {
    ch := make(chan int)
    go produce(ch)

    // range 自动接收,直到通道关闭
    for v := range ch {
        fmt.Println(v) // 1 2 3
    }

    // 关闭后的接收立即返回零值与 false
    v, ok := <-ch
    fmt.Println(v, ok) // 0 false
}
Warning

关闭规则:只由发送方关闭;多个发送方时不要由单个发送方关闭(应引入额外的"完成"信号通道)。接收方永远不需要关闭通道。

select:多路复用

select 同时等待多个通道操作,就绪者随机选择执行;均未就绪时阻塞,含 default 时立即走默认分支:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() { time.Sleep(100 * time.Millisecond); ch1 <- "来自服务 A" }()
    go func() { time.Sleep(50 * time.Millisecond); ch2 <- "来自服务 B" }()

    // 两个通道同时监听,先到先得
    for i := 0; i < 2; i++ {
        select {
        case msg := <-ch1:
            fmt.Println(msg)
        case msg := <-ch2:
            fmt.Println(msg)
        }
    }

    // 非阻塞探测 + 超时控制
    done := make(chan struct{})
    go func() { time.Sleep(time.Second); done <- struct{}{}; close(done) }()

    select {
    case <-done:
        fmt.Println("任务完成")
    case <-time.After(2 * time.Second):
        fmt.Println("等待超时")
    }
}

select 配合 time.After 是实现操作超时的标准手法,与 context 的超时机制互补。

方向限定

函数签名中可限定通道方向,由编译器防止误用:

func producer(out chan<- int) { /* 只能发送 */ }
func consumer(in <-chan int)  { /* 只能接收 */ }

并发模式:流水线与 fan-in

package main

import "fmt"

// 生成阶段
func generate(nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for _, n := range nums {
            out <- n
        }
    }()
    return out
}

// 加工阶段:平方
func square(in <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for n := range in {
            out <- n * n
        }
    }()
    return out
}

func main() {
    // 流水线:生成 -> 平方 -> 消费
    for v := range square(generate(1, 2, 3, 4)) {
        fmt.Println(v) // 1 4 9 16
    }
}

小结

  • channel 是类型安全的通信管道,无缓冲强调同步交接,有缓冲提供解耦。
  • 通道由发送方关闭;rangev, ok := <-ch 感知关闭。
  • select 是多路复用与超时控制的核心;方向限定提升 API 安全性。