Search code examples
gochannelgoroutine

A simple example about Go Channel with deadlock and why


My code as follows:

package main

import (
    "fmt"
)

func main() {
    c1 := make(chan int)
    fmt.Println("push c1: ")
    c1 <- 10
    g1 := <- c1
    fmt.Println("get g1: ", g1)
}

when I debug with delve, it prints this result:

push c1:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
        D:/Go/projects/hello-world/src/ch9/code9_6/code1.go:10 +0xde
Process 6276 has exited with status 2

I don't know why, it's just one simple channel example, I make one channel, send value to it and get value from it, just this, someone can tell me why, and how to correct it, Thx a lot.


Solution

  • Quote from 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.

    Channels are meant to work between goroutines, but you have only 1. So c1 <- 10 blocks execution, until someone (usually in other goroutine) receives the value.

    To fix that:

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        c1 := make(chan int)
    
        go func() {
            g1 := <- c1 // wait for value
            fmt.Println("get g1: ", g1)
        }()
    
        fmt.Println("push c1: ")
        c1 <- 10 // send value and wait until it is received.
    }
    

    Try executing it in The Go Playground.

    I suggest you go through official Go concurrency tour starting from https://tour.golang.org/concurrency/1

    EDIT: Another option is to use Buffered channel, like below. However making Buffered channel does not mean it has non-blocking send/receive operations. It just means it will block send after N values in queue, where N is always predefined number. Internally it will store sent values into an array, and block until values are received (like non-buffered channel) when that array is filled.

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        c1 := make(chan int, 1) // buffers 1 value without blocking.
        fmt.Println("push c1: ")
        c1 <- 10
        g1 := <- c1
        fmt.Println("get g1: ", g1)
    }
    

    Try it on Go Playground.