Mark
Mark

Reputation: 99

golang channels deadlock before receiving values

I'm not understanding why this doesn't work https://play.golang.org/p/_ALPii0pXV6 but this https://play.golang.org/p/vCOjAr-o54e works.

As I understand the goroutine asynchronously sends to value true to a and 12 to b. While in the main function, a is blocked, until it receives a value. Why is it that when I rearrange it to have b is blocked before a, it results in a deadlock?

Upvotes: 2

Views: 80

Answers (3)

Mxb
Mxb

Reputation: 101

If you rewrite the code the way it is going to be executed sequentially then it becomes clearer what's going on.

Original code:

func main() {
    a := make(chan bool)
    b := make(chan int64)
    go func(a chan bool, b chan int64) {
        fmt.Println("Here1")
        a <- true
        b <- 12
    } (a,b)
    fmt.Println("Here2")
    fmt.Println(fmt.Sprintf("%d", <-b))
    fmt.Println(fmt.Sprintf("%v", <-a))
}

Close representation of sequential execution of the same code:


    a := make(chan bool)
    b := make(chan int64)
    fmt.Println("Here2") // Prints

    // Pass control to new goroutine
    fmt.Println("Here1")
    a <- true     // Write to channel a and block goroutine here and pass control to main

    fmt.Println(fmt.Sprintf("%d", <-b)) // Tries to read from b but nothing has been written to it so blocks. At this point all your goroutines are blocked hence the deadlock.

    fmt.Println(fmt.Sprintf("%v", <-a)) // doesn't even reach here.
    b <- 12
}

Upvotes: 1

Kyle
Kyle

Reputation: 1170

Go by Example explains that, by default, channel sending and receiving waits until both the sending routine and the receiving routine are ready. This blocking is made obvious by the following example:

func main() {
    ch := make(chan int)
    ch <- 1
    fmt.Println(<-ch)
}

This code results in a deadlock because the only goroutine (the main one) is stuck at ch <- 1, waiting for another goroutine to receive. Little does it know that we are expecting it to be the receiver at the next line.

This explains why your first example does not work, because the other goroutine doesn't send on b until its send operation on a has completed. But the main routine won't receive on a until it's received on b! So both are stuck waiting forever.

To read more about this kind of operation (called a synchronous operation), check out this explanation.

Upvotes: 3

Zan Lynx
Zan Lynx

Reputation: 54325

Go channels are unbuffered by default. That means that it cannot send on a channel until the receiver is reading the channel. This is actually the Go preferred mode. It's more efficient than buffered channels in most cases.

What that means for your first code is that the goroutine cannot proceed to write to channel b until it completes the write to channel a. It cannot do that until the main goroutine reads a.

Upvotes: 3

Related Questions