繁体   English   中英

读取并发goroutine中写入的通道时缺少最后一个值

[英]Last value is missing when reading channel written in concurrent goroutines

我在Go中相当新,我希望异步运行几个任务,等待所有这些任务完成并将结果收集到一个切片中。

我正在阅读大量文档和示例,特别是Nathan LeClaire的帖子 ,并提出了一些接近我想做的事情(见下面的代码)。 机制很简单:

  • 触发10个goroutine,每个goroutine在通道中写入一个值。
  • 另一个goroutine读取通道并填充切片。
  • 完成所有这些操作后,将打印切片。

然而,结果显示9长度切片(值从0到8)和第10个值(应该是9)似乎缺失。 程序退出很好,我不知道发生了什么。 任何提示都表示赞赏。

这是一个与http://play.golang.org/p/HUFOZLmCto一起玩的代码示例:

package main

import (
    "fmt"
    "sync"
)

func main() {

    var wg sync.WaitGroup

    n := 10    
    c := make(chan int)

    wg.Add(n)

    for i := 0; i < n; i++ {
        go func(val int) {
            defer wg.Done()
            fmt.Println("Sending value to channel: ", val)
            c <- val
        }(i)
    }

    var array []int

    go func() {
        for val := range c {
            fmt.Println("Recieving from channel: ", val)
            array = append(array, val)
        }
    }()

    wg.Wait()
    fmt.Println("Array: ", array)
}

这就是结果:

Sending value to channel:  0
Recieving from channel:  0
Sending value to channel:  1
Recieving from channel:  1
Sending value to channel:  2
Recieving from channel:  2
Sending value to channel:  3
Recieving from channel:  3
Sending value to channel:  4
Recieving from channel:  4
Sending value to channel:  5
Recieving from channel:  5
Sending value to channel:  6
Recieving from channel:  6
Sending value to channel:  7
Recieving from channel:  7
Sending value to channel:  8
Recieving from channel:  8
Sending value to channel:  9
Array:  [0 1 2 3 4 5 6 7 8]     // Note 9 is missing here

您在接收goroutine有机会接收并处理该值之前退出。 array变量上还存在竞争条件,其中main可能会尝试在append操作期间打印数组。

请注意,即使使用无缓冲通道会在两个循环之间创建同步点,并保证接收循环具有wg.Done()之前的值,也不能保证fmt.Printlnappendmain继续之前发生。

安排这个的一种方法是将接收循环放在main中,然后在它自己的goroutine中等待关闭c chan

go func() {
    wg.Wait()
    close(c)
}()

for val := range c {
    fmt.Println("Recieving from channel: ", val)
    array = append(array, val)
}

http://play.golang.org/p/YReTVZtsUv

很简单。 在某些时候,所有值都被写入,waitGroup被释放并且goroutine正在填充sice。 由于waitGroup是释放的,因此可以在将通道排入切片之前进行打印。

要解决,请将wg.Done()移动到阅读器中,以防止在排水之前发生打印。

package main

import (
    "fmt"
    "sync"
)

func main() {
    n := 10
    c := make(chan int)

    var wg sync.WaitGroup
    wg.Add(10)

    for i := 0; i < n; i++ {
        go func(val int) {
            fmt.Println("Sending value to channel: ", val)
            c <- val
        }(i)
    }

    var array []int

    go func() {

        for val := range c {
            fmt.Println("Recieving from channel: ", val)
            array = append(array, val)
            wg.Done()
        }
    }()

    wg.Wait()
    fmt.Println("Array: ", array)
}

操场上的例子

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM