使用通道发送到多个切片时数据丢失

hui*_*osa 3 concurrency go

我最近正在学习 golang 中的并发,我正在考虑一个程序,生成一系列数字,然后将它们同时发送到三个切片。这是代码:

func main() {
    ch := make(chan int)
    done := make(chan bool)
    var bag1 []int
    var bag2 []int
    var bag3 []int

    go func() {
        for i := 0; i < 1000000; i++ {
            ch <- i
        }
        close(ch)
        done <- true
    }()

    go sendToBag(&bag1, ch)
    go sendToBag(&bag2, ch)
    go sendToBag(&bag3, ch)

    <-done

    len1 := (len(bag1))
    len2 := (len(bag2))
    len3 := (len(bag3))

    fmt.Println("length of bag1:", len1)
    fmt.Println("length of bag2:", len2)
    fmt.Println("length of bag3:", len3)
    fmt.Println("total length:", len1+len2+len3)
}

func sendToBag(bag *[]int, ch <-chan int) {
    for n := range ch {
        *bag = append(*bag, n)
    }
}
Run Code Online (Sandbox Code Playgroud)

其输出如下所示:

length of bag1: 327643
length of bag2: 335630
length of bag3: 336725
total length: 999998
Run Code Online (Sandbox Code Playgroud)

三个切片的总长度并不总是等于发送给它们的数字计数。所以我的问题是:是什么导致了问题以及如何改进代码

mko*_*iva 5

您对通道的使用done根本不足以保证所有 3 个sendToBaggoroutine 完全完成其工作。

虽然通道确实被执行之前的for n := range ch {语句“完全耗尽” ,但代码中没有任何内容可以确保在执行final之后执行该语句,因此不能保证对包的调用将被执行仅在通话之后 <-done<-done*bag = append(*bag, n)len()append()

使用等待组而不是完成通道。

func main() {
    ch := make(chan int)
    wg := sync.WaitGroup{}
    var bag1 []int
    var bag2 []int
    var bag3 []int

    go func() {
        for i := 0; i < 1000000; i++ {
            ch <- i
        }
        close(ch)
    }()

    wg.Add(3)
    go sendToBag(&bag1, ch, &wg)
    go sendToBag(&bag2, ch, &wg)
    go sendToBag(&bag3, ch, &wg)

    wg.Wait()

    len1 := (len(bag1))
    len2 := (len(bag2))
    len3 := (len(bag3))

    fmt.Println("length of bag1:", len1)
    fmt.Println("length of bag2:", len2)
    fmt.Println("length of bag3:", len3)
    fmt.Println("total length:", len1+len2+len3)
}

func sendToBag(bag *[]int, ch <-chan int, wg *sync.WaitGroup) {
    for n := range ch {
        *bag = append(*bag, n)
    }
    wg.Done()
}
Run Code Online (Sandbox Code Playgroud)

https://go.dev/play/p/_wDgS5aS7bI