Question

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.

Was it helpful?

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.

OTHER TIPS

val has given a good answer. I'd like to add an extra tip I've found useful.

When learning to use goroutines, use zero-buffered channels to start with. This way, when you make a mistake, you'll immediately get a deadlock, which you can learn from. You need to learn how to write code that doesn't deadlock, which means learning tricks like not having cyclic dependencies in the client-server relationships (assuming your goroutines are written as clients or servers).

Reasoning about the network without buffering is simpler, although this might not be obvious at first.

Buffering is really useful, but should be considered as a means to enhance performance.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top