Goroutine does not execute if time.Sleep included

Issue

The following code runs perfectly fine:

package main

import (
    "fmt"
)

func my_func(c chan int){
    fmt.Println(<-c)
}

func main(){
    c := make(chan int)
    go my_func(c)

    c<-3
}

playgound_1

However if I change

c<-3

to

time.Sleep(time.Second)
c<-3

playground_2

My code does not execute.

My gut feeling is that somehow main returns before the my_func finishes executing, but it seems like adding a pause should not have any effect. I am totally lost on this simple example, what’s going on here?

Solution

When the main function ends, the program ends with it. It does not wait for other goroutines to finish.

Quoting from the Go Language Specification: Program Execution:

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

So simply when your main function succeeds by sending the value on the channel, the program might terminate immediately, before the other goroutine has the chance to print the received value to the console.

If you want to make sure the value gets printed to the console, you have to synchronize it with the event of exiting from the main function:

Example with a “done” channel (try it on Go Playground):

func my_func(c, done chan int) {
    fmt.Println(<-c)
    done <- 1
}

func main() {
    c := make(chan int)
    done := make(chan int)
    go my_func(c, done)

    time.Sleep(time.Second)
    c <- 3
    <-done
}

Since done is also an unbuffered channel, receiving from it at the end of the main function must wait the sending of a value on the done channel, which happens after the value sent on channel c has been received and printed to the console.

Explanation for the seemingly non-deterministic runs:

Goroutines may or may not be executed parallel at the same time. Synchronization ensures that certain events happen before other events. That is the only guarantee you get, and the only thing you should rely on.
2 examples of this Happens Before:

  • The go statement that starts a new goroutine happens before the goroutine’s execution begins.
  • A send on a channel happens before the corresponding receive from that channel completes.

For more details read The Go Memory Model.

Back to your example:

A receive from an unbuffered channel happens before the send on that channel completes.

So the only guarantee you get is that the goroutine that runs my_func() will receive the value from channel c sent from main(). But once the value is received, the main function may continue but since there is no more statements after the send, it simply ends – along with the program. Whether the non-main goroutine will have time or chance to print it with fmt.Println() is not defined.

Answered By – icza

Answer Checked By – Cary Denson (GoLangFix Admin)

Leave a Reply

Your email address will not be published.