Golang 通道缺少一些值

Golang channel missing some values

我有以下代码

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    concurrent := 12
    ch := make(chan int)
    defer close(ch)
    for i := 1; i <= concurrent; i++ {
        go worker(i, ch)
    }
    for i := 1; i <= 21; i++ {
        ch <- i
    }
}

func worker(worker int, ch chan int) {
    for requestId := range ch {
        fmt.Printf("worker %d is requesting to %d...\n", worker, requestId)
        time.Sleep(time.Duration(rand.Intn(3)) * time.Second)
    }
}

main函数启动多个goroutine运行worker函数然后把一些值放到channel中,由worker打印出来。 我的问题是有时通道中的最后一个值或更多值不是由工作人员打印的。

worker 10 is requesting to 10...
worker 5 is requesting to 1...
worker 5 is requesting to 13...
worker 7 is requesting to 6...
worker 2 is requesting to 2...
worker 3 is requesting to 7...
worker 3 is requesting to 14...
worker 6 is requesting to 4...
worker 11 is requesting to 9...
worker 9 is requesting to 8...
worker 9 is requesting to 15...
worker 12 is requesting to 11...
worker 8 is requesting to 12...
worker 8 is requesting to 16...
worker 4 is requesting to 5...
worker 1 is requesting to 3...
worker 11 is requesting to 17...

我认为这是因为 main 函数结束并在打印最后一个值之前“杀死”所有正在运行的 goroutine,因为最后一个睡眠总是打印所有值。

func main() {
    concurrent := 12
    ch := make(chan int)
    defer close(ch)
    for i := 1; i <= concurrent; i++ {
        go worker(i, ch)
    }
    for i := 1; i <= 21; i++ {
        ch <- i
    }
    time.Sleep(3 * time.Second)
}

如何在不使用 sleep 并且尽可能只使用频道的情况下解决这个问题?

因为主 goroutine 在最后一个 for 循环后立即退出导致整个程序也退出,后台 goroutines 可能有也可能没有机会 运行,你必须提供一些同步方式来“等待”你所有的 worker goroutines 完成。

使用sync.WaitGroup

   func main() {

    concurrent := 12
    ch := make(chan int)

    var wg sync.WaitGroup
    for i := 1; i <= concurrent; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            worker(i, ch)
        }(i) // You have to pass i as parameter
    }
    for i := 1; i <= 21; i++ {
        ch <- i
    }
    close(ch) // Close channel to tell all workers to stop

    wg.Wait() // Wait all workers to finish its work
   }