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

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

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条)

报告相同问题?

悬赏问题

  • ¥15 Power query添加列问题
  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 有没有帮写代码做实验仿真的
  • ¥15 報錯:Person is not mapped,如何解決?
  • ¥30 vmware exsi重置后登不上
  • ¥15 易盾点选的cb参数怎么解啊
  • ¥15 MATLAB运行显示错误,如何解决?
  • ¥15 c++头文件不能识别CDialog
  • ¥15 Excel发现不可读取的内容
  • ¥15 关于#stm32#的问题:CANOpen的PDO同步传输问题