Go channels seem not to be blocking although supposed to be

Issue

I am new to golang and have a hard time understanding how the channels work. My understanding is that by default, channels are supposed to be blocking, so I expect a goroutine that writes into a channel to be frozen by the scheduler until an other goroutine reads the channel content. So I tried the following code, which gives the corresponding output:

package main

import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup
var v int

func main() {
    ch := make(chan string)
    wg.Add(2)
    go func(ch chan string) {
        fmt.Println("Ready to receive")
        for msg := range ch {
            fmt.Println("received: ", msg)
            fmt.Println(v)
        }
        wg.Done()
    }(ch)
    go func(ch chan string) {
        fmt.Println("Will send the SMS to mama")
        ch <- "msg 1"
        v += 1
        fmt.Println("Done! sent the message 1")
        ch <- "msg 2"
        v += 1
        fmt.Println("Done! sent the message 2")
        ch <- "msg 3"
        v += 1
        fmt.Println("Done! sent the message 3")
        close(ch)
        wg.Done()
    }(ch)

    wg.Wait()
}

output:

Will send the SMS to mama
Ready to receive
received:  msg 1
0
Done! sent the message 1
Done! sent the message 2
received:  msg 2
2
received:  msg 3
2
Done! sent the message 3

I am a bit surprised as I was expecting the following order:

  1. msg 1 sent
  2. msg 1 received
  3. msg 2 sent
  4. msg 2 received

and so on. But this is obviously not the case.

Does someone have any idea on why Go behaves this way? Many thanks,

Here is a link to the code https://play.golang.org/p/O6SXf0CslPf. And here are my sources for stating what I said earlier: https://medium.com/rungo/anatomy-of-channels-in-go-concurrency-in-go-1ec336086adb https://rakyll.org/scheduler/

Solution

This behaviour is completely normal so to answer your question

so I expect a goroutine that writes into a channel to be frozen by the scheduler until an other goroutine reads the channel content

Schedular may or may not continue that same goroutine after value is sent through channel unless further synchronization is needed.

So for example After “msg 2” is sent to ch and is read in another goroutine in the following line

ch <- "msg 2"

goroutine can continue to execute v += 1 and call fmt.Println before other goroutine calls it.

Also calls to fmt.Println from different goroutines requires synchronization and possibly mutex calls which may also reorder print statements.

Further more there is data race on variable v

Answered By – Gaurav Dhiman

Answer Checked By – Dawn Plyler (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.