通道并发问题

3
我正在尝试使用 Go 中的通道(channel)概念。我编写了下面这个程序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)

    }
}

我希望至少能得到一些输出,但我完全没有得到任何输出。

1
在退出 main 函数之前缺少 wg.Wait()。 - tristan
@tristan 你说得对。我加了那个语句。但是它会产生“所有goroutine都处于休眠状态-死锁!”的错误。 - Cholthi Paul Ttiopic
你的goroutine立即等待在ch上接收一个值。但是没有任何东西在ch上发送任何值。 - Adrian
2个回答

8
main()函数(即main goroutine)结束时,您的程序也会结束(它不会等待其他非main goroutine完成)。您必须在结尾处添加wg.Wait()调用。请参见No output from goroutine in Go

一旦这样做,您将遇到死锁。这是因为所有goroutine都要从通道接收值,然后才能发送内容。

所以您应该首先向通道发送一些内容,以让至少一个goroutine继续执行。

一旦这样做,您就会看到数字打印了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上进行尝试。

谢谢,这非常有教育意义。 - Cholthi Paul Ttiopic

1
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)
}

欢迎来到SO,请在代码中添加一些解释,这将有助于您的答案更受SO成员的接受。 - dkb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接