No output from goroutine in Go

有些话、适合烂在心里 提交于 2019-12-16 19:55:14

问题


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



回答3:


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


来源:https://stackoverflow.com/questions/28958192/no-output-from-goroutine-in-go

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!