Search code examples
gochannelgoroutinecapacity

Is gorouines ignore channel's buffer size


Environment: OS X 10.8, Go 1.0.2

I make a channel with buffer-size 2, then if I write channel three times, it will throw error:

throw: all goroutines are asleep - deadlock!

Of course, it's correct.

BUT if I write channel four or more times in the goroutines, it works fine, why? The channel's capacity is 2, why goroutines ignore that or forget the capacity setting? I comment the read-channel codes, so no one will read channel and save the capacity. I also use time.Sleep to waiting for all goroutines finish their work.

please review following codes: package main

//import "fmt"

func main() {
    c := make(chan int, 2)
    /*c <- 1
    c <- 2
    c <- 3*/
    for i:=0; i<4; i++ {
        go func(i int) {
            c <- i
            c <- 9
            c <- 9
            c <- 9
        }(i)
    }
    time.Sleep(2000 * time.Millisecond)

    /*for i:=0; i<4*2; i++ {
        fmt.Println(<-c)
    }*/
}

Would anyone please give some hits? thanks, guys.


Solution

  • When a channel is buffered, this means it will not block until the buffer is full. Once the buffer is full, the sending goroutine will block as it tries to add things to the channel.

    This means that this will block:

    c := make(chan int)
    c <- 1          // Block here, this is unbuffered !
    println(<-c)
    

    And this will also block:

    c := make(chan int, 2)
    c <- 1
    c <- 2
    c <- 3           // Block here, buffer is full !
    println(<-c)
    

    But the point of goroutines and channel is precisely to run things concurrently, so this will work:

    c := make(chan int)
    go func() { c <- 1; }() // This will block in the spawned goroutine until...
    println(<-c)            // ... this line is reached in the main goroutine
    

    And similarly:

    c := make(chan int, 2)
    go func() {  // `go ...` spawns a goroutine
        c <- 1   // Buffer is not full, no block
        c <- 2   // Buffer is not full, no block
        c <- 3   // Buffer is full, spawned goroutine is blocking until...
    }()
    println(<-c) // ... this line is reached in the main goroutine
    

    In your example, you spawn four different goroutines, which all write four numbers to the same buffered channel. As the buffer is 2 < 16, they will end up blocking

    But the crux of the matter is that the Go policy is to wait only for the main goroutine:

    Program execution begins by initializing the main package and then invoking the function main. When the function main returns, the program exits. It does not wait for other (non-main) goroutines to complete.

    This means that in your first example, the main goroutine was blocking when it reached line c <- 3. As no other goroutine was able to do anything which could potentially unblock it, the runtime detected that the program was deadlocked and reported an error.

    In your second example however, spawned goroutines block, while the main continues quietly until it reaches the end of its execution, at which point all the (blocked) spawned goroutines are silently killed, and no error is reported.