通道并发问题

Channels concurrency issue

我正在试验 Go 中的通道概念。我写了下面的程序 playground 来实现使用通道的计数器。但是我没有得到任何输出,尽管我在 goroutine 主体中进行了一些打印。

func main() {
    wg := sync.WaitGroup{}
    ch := make(chan int)

    count := func(ch chan int) {
        var last int
        last = <-ch
        last = last + 1
        fmt.Println(last)
        ch <- last
        wg.Done()
    }

    wg.Add(10)
    for i := 1; i <= 10; i++ {
        go count(ch)

    }
}

我希望至少有一些输出,但我得到的是 none。

main() 函数(main goroutine)结束时,您的程序也会结束(它不会等待其他非 main goroutine 完成)。您必须在末尾添加一个 wg.Wait() 调用。参见

一旦你这样做,你就会陷入僵局。这是因为所有的 goroutines 都是从尝试从通道接收一个值开始的,然后才会发送一些东西。

所以你应该先在频道上发送一些东西让至少一个 goroutines 继续。

执行此操作后,您会看到数字打印 10 次,然后再次出现死锁。这是因为当最后一个 goroutine 尝试发送其递增的数字时,将没有人接收到它。解决这个问题的一种简单方法是为通道提供缓冲区。

最终的工作示例:

wg := sync.WaitGroup{}
ch := make(chan int, 2)

count := func(ch chan int) {
    var last int
    last = <-ch
    last = last + 1
    fmt.Println(last)
    ch <- last
    wg.Done()
}

wg.Add(10)
for i := 1; i <= 10; i++ {
    go count(ch)

}
go func() {
    ch <- 0
}()
wg.Wait()

输出(在 Go Playground 上尝试):

1
2
3
4
5
6
7
8
9
10

另请注意,由于我们对通道进行了缓冲,因此没有必要使用另一个 goroutine 来发送初始值,我们可以在 main goroutine 中这样做:

ch <- 0
wg.Wait()

这将输出相同的结果。在 Go Playground.

上试用
func main() {
    wg := sync.WaitGroup{}
    ch := make(chan int)

    count := func(ch chan int) {
        var last int
        last, ok := <-ch // 这里做一层保护
        if !ok {
            return
        }

        last = last + 1
        fmt.Println(last)

        go func(ch chan int, res int) {
            ch <- res
        }(ch, last)

        wg.Done()
    }

    go func() {
        ch <- 0
    }()

    wg.Add(10)
    for i := 1; i <= 10; i++ {
        go count(ch)
    }

    wg.Wait()

    fmt.Println("main finish")
    close(ch)
}