You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何使用无缓冲channel时main() goroutine发送最后一个值后未阻塞?

Go无缓冲Channel场景下main Goroutine提前退出导致输出缺失的问题

你编写的Go代码如下:

package main

import (
    "fmt"
)

func squares(c chan int) {
    for i := 0; i < 4; i++ {
        num := <-c
        fmt.Println(num * num)
    }
}

func main() {
    fmt.Println("main() started")
    c := make(chan int)

    go squares(c)

    c <- 1
    c <- 2
    c <- 3
    c <- 3
    fmt.Println("main() stopped")
}

实际输出:

main() started
1
4
9
main() stopped

预期输出是1、4、9、9,并且存在疑问:使用无缓冲Channel时,main goroutine为何在发送最后一个值后未阻塞?添加time.Sleep后程序符合预期。


问题原因

  1. 无缓冲Channel的发送逻辑:无缓冲Channel的发送操作确实会阻塞,直到接收方完成接收。你遇到的情况不是发送未阻塞,而是发送完成后,main goroutine没有等待squares goroutine完成后续的打印操作就直接退出了。
  2. main goroutine的特殊性:main goroutine是Go程序的入口,一旦它执行完所有语句,整个程序会立即终止,所有其他goroutine都会被强制终止,不管它们是否完成剩余任务。在你的代码中,main发送第四个值后,squares已经接收到了这个值,但还没来得及打印9,main就执行了fmt.Println("main() stopped")并退出,导致最后一次输出被截断。
  3. time.Sleep的作用:添加time.Sleep只是给了squares goroutine足够的时间完成最后一次打印,属于临时的 workaround,不是可靠的同步方式。

解决方案

方案1:使用额外的Channel同步

通过一个额外的done Channel,让main等待squares完成所有任务后再退出:

package main

import (
    "fmt"
)

func squares(c chan int, done chan bool) {
    for i := 0; i < 4; i++ {
        num := <-c
        fmt.Println(num * num)
    }
    done <- true // 发送任务完成信号
}

func main() {
    fmt.Println("main() started")
    c := make(chan int)
    done := make(chan bool)

    go squares(c, done)

    c <- 1
    c <- 2
    c <- 3
    c <- 3
    <-done // 阻塞等待squares完成
    fmt.Println("main() stopped")
}

方案2:使用sync.WaitGroup

适合管理多个goroutine的场景,通过WaitGroup等待所有任务完成:

package main

import (
    "fmt"
    "sync"
)

func squares(c chan int, wg *sync.WaitGroup) {
    defer wg.Done() // 任务结束时标记完成
    for i := 0; i < 4; i++ {
        num := <-c
        fmt.Println(num * num)
    }
}

func main() {
    fmt.Println("main() started")
    c := make(chan int)
    var wg sync.WaitGroup

    wg.Add(1) // 注册一个需要等待的goroutine
    go squares(c, &wg)

    c <- 1
    c <- 2
    c <- 3
    c <- 3
    wg.Wait() // 等待所有注册的任务完成
    fmt.Println("main() stopped")
}

总结

无缓冲Channel的发送操作本身是阻塞的,你遇到的输出缺失问题本质是main goroutine提前退出导致的。使用Channel或WaitGroup进行显式同步,才是可靠的解决方式,避免依赖time.Sleep这类不可靠的手段。

内容的提问来源于stack exchange,提问作者Orazbayeva Dana

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 22:20:23