13

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?

icza
  • 389,944
  • 63
  • 907
  • 827
Akavall
  • 82,592
  • 51
  • 207
  • 251

1 Answers1

15

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.

icza
  • 389,944
  • 63
  • 907
  • 827
  • 2
    Can you explain why in my first example `my_func` finishes before main ends, but not in the second example? – Akavall Feb 03 '15 at 20:13
  • I think this is the right answer, but might be better if some info was added re: a way (or ways) to get the example code to wait ... – Digikata Feb 03 '15 at 20:14
  • Added explanation and an example how to make sure the value sent on the channel gets printed. – icza Feb 03 '15 at 20:42
  • I think I get it, but I want to make sure. Is my understanding correct: "both first and second example, my goroutine might or might not be able to complete before main finishes, and the fact that it happens in first example and not second is just pure chance. – Akavall Feb 03 '15 at 20:45
  • 5
    @Akavall Yes, in both of those cases the value might or might not get printed. Note that on the Go Playground the results of programs are cached. So if in one case it gets printed, running the same program will always print it (until someone deletes the output caches of the programs). – icza Feb 03 '15 at 20:47