dongxin991209
2018-07-26 02:54
浏览 33
已采纳

为什么我的频道需要缓冲区?

I'm trying to learn Go and I'm using this tutorial.

I've written the following code,

var wg sync.WaitGroup

func foo(c chan int, someValue int) {
    defer wg.Done()
    c <- someValue * 5
}

func main() {
    fooVal := make(chan int)
    for i := 0; i < 10; i++ {
        go foo(fooVal, i)
        wg.Add(1)
    }

    wg.Wait() // Wait for all routines to complete
    close(fooVal) // close channel

    for item := range fooVal {
        fmt.Println(item)
    }
}

Here is my understanding so far,

  • I create a channel that receives ints
  • I create 10 subroutines, and I add 1 to the waitgroup so I can get them to sync later
  • I wait for the routines to complete
  • I close the channel so that it doesn't receive any more values
  • I loop through the values in the channel to print them

However, I get an error that says:

fatal error: all goroutines are asleep - deadlock!

I'm not sure what this means. My guess is that range tries to get a value from the channel but it doesn't have any. But that shouldn't be happening because I waited for all routines to complete and then I closed the channel.

What's going on?

The solution for this is to do something like make(chan int, 10) to give it a buffer, but I'm not sure what a buffer is or why I need it.

Also, I'm not sure what make does. I've used it to create a map as well. Is it just a constructor?

  • 写回答
  • 好问题 提建议
  • 追加酬金
  • 关注问题
  • 收藏
  • 邀请回答

2条回答 默认 最新

  • duankeng9477 2018-07-26 03:31
    已采纳

    The reason all of your goroutines are asleep is because by default a channel blocks it's goroutine on send until the value is received. See https://tour.golang.org/concurrency/2

    By default, sends and receives block until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.

    A buffer resolves this by allowing a channel to 'hold' that many values without blocking. Think of a buffered channel as a bucket that can hold N items (where N is the buffer size, 10 in your case) in order to put more into the bucket, you need to wait for something to be removed (i.e. read). By default a channel is unbuffered, and it's value must be received before it will unblock it's sending goroutine.

    In your code you have goroutines attempting to put items into your unbuffered channel, so the first routine waits for the item it put into the channel to be read. But your channel will never empty until the range statement reads it, which will never fire until the channel empties and the goroutine finishes (because of wg.Wait()), which is a deadlock. Neither can proceed.

    With a buffer, the buffered channel tells your goroutines it's ok to finish without it's value being read (up to 10 items in your case) and so the waitgroup finishes, and the range statement reads all the values out as expected.

    As for make, thinking of it as a constructor is good enough, but know that it's only used for slices, channels and maps. Those are special types in go and can't be created like regular structs. (I believe it has to do with the fact that each of those acts generically, i.e. it can hold items of any specified type, and that's why make is needed, but I'm not 100% on that).

    评论
    解决 无用
    打赏 举报
查看更多回答(1条)

相关推荐 更多相似问题