By Dinesh Panchananam


2015-03-10 07:28:47 8 Comments

While SayHello() executes as expected, the goroutine prints nothing.

package main

import "fmt"

func SayHello() {
    for i := 0; i < 10 ; i++ {
        fmt.Print(i, " ")
    }
}

func main() {
    SayHello()
    go SayHello()
}

3 comments

@dolan 2019-05-18 20:56:38

As other's have mentioned, Go program exit when the main function returns.

One option is to use something like sync.WaitGroup to wait on the other goroutines that main has spawned before returning from main.

Another option is to call runtime.Goexit() in main. From the godoc:

Goexit terminates the goroutine that calls it. No other goroutine is affected. Goexit runs all deferred calls before terminating the goroutine. Because Goexit is not a panic, any recover calls in those deferred functions will return nil.

Calling Goexit from the main goroutine terminates that goroutine without func main returning. Since func main has not returned, the program continues execution of other goroutines. If all other goroutines exit, the program crashes.

This allows main goroutine to stop executing while the background routines continue to execute. For example:

package main

import (
    "fmt"
    "runtime"
    "time"
)

func f() {
    for i := 0; ; i++ {
        fmt.Println(i)
        time.Sleep(10 * time.Millisecond)
    }
}

func main() {
    go f()
    runtime.Goexit()
}

This can be cleaner than blocking forever in the main function, especially for programs that are infinite. One downside is that if all of the goroutines of a process return or exit (including the main goroutine), Go will detect this as an error and panic:

fatal error: no goroutines (main called runtime.Goexit) - deadlock!

To avoid this, at least one goroutine must call os.Exit before it returns. Calling os.Exit(0) immediately terminates the program and indicates that it did so without error. For example:

package main

import (
    "fmt"
    "os"
    "runtime"
    "time"
)

func f() {
    for i := 0; i < 10; i++ {
        fmt.Println(i)
        time.Sleep(10 * time.Millisecond)
    }
    os.Exit(0)
}

func main() {
    go f()
    runtime.Goexit()
}

@icza 2015-03-10 07:32:45

When your main() function ends, your program ends as well. 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.

See this answer for more details.

You have to tell your main() function to wait for the SayHello() function started as a goroutine to complete. You can synchronize them with channels for example:

func SayHello(done chan int) {
    for i := 0; i < 10; i++ {
        fmt.Print(i, " ")
    }
    if done != nil {
        done <- 0 // Signal that we're done
    }
}

func main() {
    SayHello(nil) // Passing nil: we don't want notification here
    done := make(chan int)
    go SayHello(done)
    <-done // Wait until done signal arrives
}

Another alternative is to signal the completion by closing the channel:

func SayHello(done chan struct{}) {
    for i := 0; i < 10; i++ {
        fmt.Print(i, " ")
    }
    if done != nil {
        close(done) // Signal that we're done
    }
}

func main() {
    SayHello(nil) // Passing nil: we don't want notification here
    done := make(chan struct{})
    go SayHello(done)
    <-done // A receive from a closed channel returns the zero value immediately
}

Notes:

According to your edits/comments: if you want the 2 running SayHello() functions to print "mixed" numbers randomly: you have no guarantee to observe such behaviour. Again, see the aforementioned answer for more details. The Go Memory Model only guarantees that certain events happen before other events, you have no guarantee how 2 concurrent goroutines are executed.

You might experiment with it, but know that the result will not be deterministic. First you have to enable multiple active goroutines to be executed with:

runtime.GOMAXPROCS(2)

And second you have to first start SayHello() as a goroutine because your current code first executes SayHello() in the main goroutine and only once it finished starts the other one:

runtime.GOMAXPROCS(2)
done := make(chan struct{})
go SayHello(done) // FIRST START goroutine
SayHello(nil) // And then call SayHello() in the main goroutine
<-done // Wait for completion

@VonC 2015-03-10 07:40:03

Right, I just understood your edit after reading dave.cheney.net/2014/03/19/channel-axioms: A send to a nil channel blocks forever, A receive from a nil channel blocks forever.

@Dinesh Panchananam 2015-03-10 08:00:49

@icza please check edits.

@icza 2015-03-10 08:03:21

@DineshPanchananam What do you mean by "unorderly" fashion? You expect to see mixed numbers randomly from the 2 running SayHello() functions?

@Dinesh Panchananam 2015-03-10 08:11:50

@icza i guess that was a dumb edit.

@icza 2015-03-10 08:28:59

@DineshPanchananam I edited my answer to address this case. But in short, you have no guarantee to observe such behavior (and even if you do, it will not be deterministic).

@Sundrique 2015-03-10 07:52:39

Alternatively (to icza's answer) you can use WaitGroup from sync package and anonymous function to avoid altering original SayHello.

package main

import (
    "fmt"
    "sync"
)

func SayHello() {
    for i := 0; i < 10; i++ {
        fmt.Print(i, " ")
    }
}

func main() {
    SayHello()

    var wg sync.WaitGroup
    wg.Add(1)

    go func() {
        defer wg.Done()
        SayHello()
    }()

    wg.Wait()
}

In order to print numbers simultaneously run each print statement in separate routine like the following

package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(fnScopeI int) {
            defer wg.Done()

            // next two strings are here just to show routines work simultaneously
            amt := time.Duration(rand.Intn(250))
            time.Sleep(time.Millisecond * amt)

            fmt.Print(fnScopeI, " ")
        }(i)
    }

    wg.Wait()
}

@Dinesh Panchananam 2015-03-10 08:01:32

please check edits

@Sundrique 2015-03-10 08:05:24

@DineshPanchananam do you want to print each number in a separate routine?

@Dinesh Panchananam 2015-03-10 08:10:52

No. i expected something ""parallel""". but i think i'm wrong.

@Sundrique 2015-03-10 08:21:27

@DineshPanchananam check the second part of the answer

@user114241 2015-03-10 14:14:36

Also, please don't use a defer when there's only one function call (the first code example) - just move wg.Done() to after the function call. There's not need to use a defer in that situation.

Related Questions

Sponsored Content

1 Answered Questions

[SOLVED] Signal goroutines to stop with channel close

  • 2018-11-12 21:43:50
  • Chen A.
  • 630 View
  • 1 Score
  • 1 Answer
  • Tags:   go channel

4 Answered Questions

[SOLVED] Why does fmt.Println inside a goroutine not print a line?

  • 2013-04-26 04:39:57
  • Ryan Bigg
  • 19384 View
  • 65 Score
  • 4 Answer
  • Tags:   go

2 Answered Questions

change variable from goroutine

  • 2018-07-11 14:15:09
  • abonec
  • 286 View
  • -4 Score
  • 2 Answer
  • Tags:   go goroutine

3 Answered Questions

[SOLVED] Stop goroutine execution on timeout

  • 2018-05-28 17:14:36
  • Pandurang Waghulde
  • 2763 View
  • 2 Score
  • 3 Answer
  • Tags:   go goroutine go-iris

3 Answered Questions

[SOLVED] Why is time.sleep required to run certain goroutines?

  • 2013-04-02 18:11:14
  • Darrrrrren
  • 12016 View
  • 26 Score
  • 3 Answer
  • Tags:   go goroutine

1 Answered Questions

[SOLVED] Different behavior of updating global variable in goroutine

  • 2017-08-10 07:23:00
  • wyhao31
  • 1387 View
  • -1 Score
  • 1 Answer
  • Tags:   go goroutine

1 Answered Questions

[SOLVED] why main goroutine always be the second to be invoked

4 Answered Questions

4 Answered Questions

[SOLVED] How do goroutines work? (or: goroutines and OS threads relation)

3 Answered Questions

[SOLVED] First goroutine example, weird results

  • 2013-09-03 19:20:45
  • tarrsalah
  • 280 View
  • 3 Score
  • 3 Answer
  • Tags:   go goroutine

Sponsored Content