0Pricing
Go Academy · 课时

无缓冲通道

Goroutine 之间的同步交接

无缓冲通道 是 CoddyKit 上的免费 Go Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Go Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Go Academy 课程共包含 4 节课。

什么是通道

通道是用于在 goroutine 之间通信的类型化管道。通道遵循 Go 的箴言:不要通过共享内存进行通信;应通过通信来共享内存。

package main
import "fmt"

func main() {
    ch := make(chan int)   // unbuffered int channel

    go func() {
        ch <- 42  // send: blocks until someone receives
    }()

    v := <-ch  // receive: blocks until someone sends
    fmt.Println(v) // 42
}

无缓冲通道的语义

无缓冲通道没有容量。发送操作会阻塞,直到接收方准备就绪;接收操作会阻塞,直到发送方发送数据。这提供了有保障的同步交接:

package main
import "fmt"

func double(in <-chan int, out chan<- int) {
    for v := range in {
        out <- v * 2
    }
    close(out)
}

func main() {
    in := make(chan int)
    out := make(chan int)
    go double(in, out)
    go func() { in <- 3; in <- 5; close(in) }()
    for v := range out { fmt.Println(v) }
    // 6
    // 10
}

将通道用作同步机制

无缓冲通道可以同步 goroutine,可将其用作工作完成的信号:

package main
import "fmt"

func compute(result chan<- int) {
    sum := 0
    for i := 1; i <= 100; i++ { sum += i }
    result <- sum
}

func main() {
    ch := make(chan int)
    go compute(ch)
    fmt.Println("Result:", <-ch) // blocks until compute sends
}

关闭通道

发送方使用close(ch)关闭通道,以表示不会再发送更多值。接收方通过双值形式检测通道是否已关闭:

package main
import "fmt"

func generate(ch chan<- int, max int) {
    for i := 0; i < max; i++ {
        ch <- i
    }
    close(ch)  // signal done
}

func main() {
    ch := make(chan int)
    go generate(ch, 5)

    for v := range ch {  // range detects close automatically
        fmt.Println(v)
    }
    fmt.Println("channel closed")
}

双值接收

双值接收v, ok := <-ch会告诉您通道是否仍处于打开状态:

package main
import "fmt"

func main() {
    ch := make(chan string, 1)
    ch <- "hello"
    close(ch)

    v1, ok1 := <-ch
    fmt.Println(v1, ok1)  // hello true

    v2, ok2 := <-ch
    fmt.Println(v2, ok2)  // "" false — channel closed
}

向已关闭的通道发送数据会触发崩溃

向已关闭的通道发送数据会触发崩溃。只有发送方应该关闭通道,接收方绝不能关闭:

package main
import "fmt"

func main() {
    ch := make(chan int)
    close(ch)

    // Receiving from closed channel returns zero value
    v, ok := <-ch
    fmt.Println(v, ok)  // 0 false

    // Sending to closed channel panics:
    defer func() { recover() }()
    ch <- 1  // panic: send on closed channel
}

单向通道

为了提高代码的清晰性和安全性,可以在函数签名中将通道限制为只发送(chan<- T)或只接收(<-chan T):

package main
import "fmt"

func producer(out chan<- int) {  // send-only
    for i := 0; i < 3; i++ { out <- i }
    close(out)
}

func consumer(in <-chan int) {   // receive-only
    for v := range in { fmt.Println(v) }
}

func main() {
    ch := make(chan int)
    go producer(ch)
    consumer(ch)
}

goroutine 与通道的往返通信

两个 goroutine 通过通道来回传递消息,这是经典的同步示例:

package main
import "fmt"

func ping(ping, pong chan struct{}) {
    for i := 0; i < 3; i++ {
        <-ping
        fmt.Println("ping")
        pong <- struct{}{}
    }
}

func main() {
    ping := make(chan struct{})
    pong := make(chan struct{})
    go func() { for { <-pong; fmt.Println("pong"); ping <- struct{}{} } }()
    go func() { for i := 0; i < 3; i++ { ping <- struct{}{}; <-pong } }()
    // Simplified version:
    fmt.Println("channels enable structured communication")
}

nil 通道会永久阻塞

从nil通道接收或向其发送数据都会永久阻塞。有时会在选择语句中有意使用这一特性:

package main
import "fmt"

func main() {
    var ch chan int  // nil channel
    fmt.Println(ch == nil) // true

    // select with nil channel — that case is never selected
    active := make(chan int, 1)
    active <- 42
    select {
    case v := <-ch:      // never selected (nil)
        fmt.Println(v)
    case v := <-active:  // selected
        fmt.Println("got:", v)
    }
}

何时使用无缓冲通道

在以下情况下可以使用无缓冲通道:

  • 需要有保障的同步交接
  • goroutine 必须确认已收到数据后,发送方才能继续
  • 实现信号量或反压
  • 构建流水线,并且每个阶段都应一次处理一个项目

快速检查

向没有准备好接收方的无缓冲通道发送数据时会发生什么?

回顾:无缓冲通道

摘要:

  • 使用make(chan T)创建——容量为零
  • 发送会阻塞,直到接收方准备就绪;接收会阻塞,直到发送方发送数据
  • 只有发送方应该关闭通道
  • range ch会迭代,直到通道关闭
  • 双值接收:v, ok := <-ch
  • 使用单向类型提高代码清晰性:chan<-和<-chan

常见问题解答

「无缓冲通道」课时是免费的吗?

是的 — 「无缓冲通道」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Go Academy 课程的其余内容,请升级到 CoddyKit PRO。 Go Academy 课程共包含 4 节课。

「无缓冲通道」这节课中我会学到什么?

Goroutine 之间的同步交接 你通过在浏览器中直接运行的动手代码来练习 Go Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Go Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Go Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「无缓冲通道」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Go Academy 课中编写并运行代码吗?

能。每节 Go Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 启动 Goroutine
  2. 无缓冲通道
  3. 有缓冲通道
  4. 通道方向与流水线模式
← 返回 Go Academy