No output from goroutine in Go

2019-01-01 01:05发布

问题:

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()
}

回答1:

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


回答2:

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()
}