Search code examples
gochannelunbuffered

Go channels unbuffered


package main 
import (
    "fmt"
    "time"
)

var message chan string

func main() {
    message = make(chan string)
    count := 6
    flag := "first"
    go func() {
        fmt.Println("child go ",flag)
        for i := 0; i < count; i++ {
            fmt.Println("set:",i)
            message <- fmt.Sprintf("message %d",i)
        }   
    }()

    time.Sleep(time.Second * 0)

    fmt.Println("main thread ",flag)
    flag = "last"
    for i := 0 ; i < count; i++ {
        fmt.Println("get:",i)
        fmt.Println(<-message)
    }
}

I run the code and the result is:

main thread  first
get: 0
child go  last
set: 0
set: 1
message 0
get: 1
message 1
get: 2
set: 2
set: 3
message 2
get: 3
message 3
get: 4
set: 4
set: 5
message 4
get: 5
message 5

I am confused by the result. Why does 'message 1' appear after 'get 1' but not 'set 2'? This has puzzled me for several days!

I think when it 'get 1', the main go routine should be blocked and the child go routine should be exec'd, send 'message 1' to channel message and continue the for loop and print 'set 2',thus blocked at send 'message 2' to channel, so main go routine start to run, output 'message 1'.


Solution

  • The code does exactly what you think it should do (if I understand what you think it should do): Receiving from message blocks until a value was sent. Unfortunately your code prints "set 2" before sending on the channel and this operation is not blocked.

    Synchronisation happens only at <-, the Println before the channel send can run. Have a look at http://play.golang.org/p/d6_SkBugqs which prints before and after each send/receive op and you'll see that all is properly sycronised: All "post-recv n" happen after "pre-send n".