Golang频道并发问题

时间:2019-04-16 11:24:40

标签: go concurrency channel goroutine

我正在尝试Golang中的频道概念。我编写了以下程序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)

    }
}

我希望至少有一些输出,但是我什么都没得到。

2 个答案:

答案 0 :(得分: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上尝试一下。

答案 1 :(得分: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)
}