简体   繁体   中英

Channel Deadlock in Go

I am getting "fatal error: all goroutines are asleep - deadlock! " for some reason in the below code. I am using buffered channel which should be non-blocking. Not sure what I am doing wrong

package main

import (
    "fmt"
    "sync"
)

func main() {
    c := make(chan int, 2)
    var wg sync.WaitGroup
    wg.Add(2)

    go doSomething(c, wg)
    go doSomething(c, wg)
    go doSomething(c, wg)

    wg.Wait()

    close(c)

    for v := range c {
        fmt.Print(v)
    }

}

func doSomething(c chan<- int, wg sync.WaitGroup) {
    defer wg.Done()
    c <- 1

}

Playground link https://play.golang.org/p/J9meD5aKna

While your solution might work I'm not happy with it.

First, the fact that you need to change the channel size to make it work indicates that there is a potential problem / bug. Now each time you want to launch another doSomething you have to remember to change channel's length.

Second, you wait until all the goroutines are finished before reading from the channel. This is kind of "waste" as one of the main points of range loop over an channel is that you don't have to wait until all the items are generated (written into channel), you can start processing the items as soon as some of them are ready.

So I would write your code as something like

func main() {
    c := make(chan int)

    var wg sync.WaitGroup
    wg.Add(3)
    go func() {
        doSomething(c)
        defer wg.Done()
    }()
    go func() {
        doSomething(c)
        defer wg.Done()
    }()
    go func() {
        doSomething(c)
        defer wg.Done()
    }()

    go func() {
        wg.Wait()
        defer close(c)
    }()

    for v := range c {
        fmt.Print(v)
    }
}

func doSomething(c chan<- int) {
    c <- 1
}

https://play.golang.org/p/T3dfiztKot

Note how the waiting and closing the channel is now in it's own goroutine - this allows to start iterating over the channel (which is now unbuffered!) right away.

I also changed the code so that the WaitGroup never leaves the scope where it is declared (ie it isn't used as parameter), this is my personal preference. I believe it makes code easier to follow and understand.

I found the problem. Actually two problems

  1. The size of the channel and wg should be 3

  2. I should pass the wg as pointer

Updated code

package main

import (
    "fmt"
    "sync"
)

func main() {
    c := make(chan int, 3)
    var wg sync.WaitGroup
    wg.Add(3)

    go doSomething(c, &wg)
    go doSomething(c, &wg)
    go doSomething(c, &wg)

    wg.Wait()

    close(c)

    for v := range c {
        fmt.Print(v)
    }

}

func doSomething(c chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    c <- 1

}

Yes, there is an important issues in your code.

you call go doSomething(c, wg) just pass wg value. you should know

Arguments in Go are always passed by value. Use a pointer when an argument may be modified.

so you should do like this go doSomething(c, &wg) , then wg in main function will be modified by defer wg.Done() in func doSomething(c chan<- int, wg sync.WaitGroup) .

.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM