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

Go程序在Goroutine完成前退出,如何正确阻塞并关闭Channel?

问题根源与解决方案

你的代码核心问题是主goroutine提前退出——主函数启动所有goroutine后,直接打印donezo就结束了。而Go程序的规则是:只要主goroutine退出,所有其他后台goroutine都会被强制终止,所以你看不到任何任务执行的输出。加time.Sleep只是临时让主goroutine多存活一段时间,给其他goroutine留了执行窗口,但这是治标不治本的办法。

同时你现有逻辑存在循环依赖:你试图让worker完成后关闭通道,但worker的for range ch循环只有在通道被关闭时才会退出,这就形成了"worker等通道关闭,通道等worker完成"的死锁闭环(如果主goroutine不提前退出的话)。


正确实现范式

要优雅完成「任务生产→worker消费→主goroutine等待全部完成」的流程,需要分两步:

  1. 等待所有任务生产者(do goroutine)完成任务并将结果发送到通道,此时可以安全关闭结果通道;
  2. 等待所有消费者worker处理完通道内的所有结果,再让主goroutine退出。

修正后的代码

package main

import (
    "fmt"
    "sync"
    "time"
)

// 模拟耗时任务
func do(num int, ch chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    time.Sleep(500 * time.Millisecond)
    ch <- num
}

// worker处理结果
func worker(id int, ch <-chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println("starting worker", id)
    for i := range ch {
        fmt.Println("worker", id, "processed:", i)
    }
}

func main() {
    results := make(chan int)
    var wgJobs sync.WaitGroup
    var wgWorkers sync.WaitGroup

    // 启动53个任务生产者
    for i := 0; i < 53; i++ {
        wgJobs.Add(1)
        go do(i, results, &wgJobs)
    }

    // 启动3个worker消费者
    for i := 0; i < 3; i++ {
        wgWorkers.Add(1)
        go worker(i, results, &wgWorkers)
    }

    // 单独goroutine:等所有任务完成后关闭通道
    go func() {
        wgJobs.Wait()
        close(results)
    }()

    // 主goroutine等待所有worker处理完结果
    wgWorkers.Wait()

    fmt.Println("donezo")
}

关键修正点

  • 新增wgJobs等待所有任务完成:每个do goroutine启动时调用wgJobs.Add(1),任务结束时defer wgJobs.Done(),确保所有结果都发送到通道后再关闭通道;
  • 用独立goroutine关闭通道:避免主goroutine阻塞在任务等待逻辑上,无法启动worker;
  • 主goroutine最后等待wgWorkers.Wait():确保所有worker处理完通道内的所有结果后,才执行退出逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 02:22:44