为了账号安全,请及时绑定邮箱和手机立即绑定

Go中没有Goroutine的输出

Go中没有Goroutine的输出

Go
喵喵时光机 2019-07-13 15:56:37
当SayHello()按预期执行,Goroutine将不打印任何内容。package mainimport "fmt"func SayHello() {     for i := 0; i < 10 ; i++ {         fmt.Print(i, " ")     }}func main() {     SayHello()     go SayHello()}
查看完整描述

3 回答

?
尚方宝剑之说

TA贡献1788条经验 获得超4个赞

当你main()函数结束,您的程序也结束了。它不会等待其他的峡谷完成。

引用Go语言规范:程序执行:

程序执行从初始化主包开始,然后调用函数。main..当函数调用返回时,程序将退出。它不会等待其他的(非-main)完成的大猩猩。

看见这个答案更多细节。

你得告诉你的main()函数等待SayHello()函数开始时是一个要完成的Goroutine。您可以将它们与通道同步,例如:

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}

另一种选择是关闭通道,发出信号表示完成:

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}

注:

根据您的编辑/注释:如果要运行2SayHello()函数随机打印“混合”数字:你不能保证观察这种行为。再次,请参阅上述答复更多细节。这个GO记忆模型只有保证某些事件发生在其他事件之前,才能保证如何执行2个并发的goroutines。

您可以尝试它,但知道结果不会是决定性的。首先,您必须允许使用以下方法执行多个活动的goroutines:

runtime.GOMAXPROCS(2)

第二,你必须先开始SayHello()因为当前代码首先执行SayHello()在主要的Goroutine中,只有当它完成之后,另一个就开始了:

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


查看完整回答
反对 回复 2019-07-13
?
不负相思意

TA贡献1777条经验 获得超10个赞

或者(对icza的回答)你可以用WaitGroup从…sync包和匿名函数以避免更改原始SayHello.

package mainimport (
    "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()}

为了同时打印数字,在单独的例程中运行每个print语句,如下所示

package mainimport (
    "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()}


查看完整回答
反对 回复 2019-07-13
?
拉风的咖菲猫

TA贡献1995条经验 获得超2个赞

正如其他人所提到的,当main函数返回。

一种选择是使用类似于sync.WaitGroup在其他的峡谷里等着main在从main.

另一个选择是runtime.Goexit()在……里面main..从戈多:

GoExit终止了调用它的Goroutine。没有其他戈鲁丁受到影响。Goexport在终止Goroutine之前运行所有延迟的呼叫。因为GoExit并不是一种恐慌,所以那些延迟函数中的任何恢复调用都会返回零。

从主Goroutine调用GoExit将终止该Goroutine,而不需要主返回。由于FuncMain尚未返回,该程序将继续执行其他goroutines。如果其他所有的Goroutines退出,程序就会崩溃。

这允许主goroutine在后台例程继续执行时停止执行。例如:

package mainimport (
    "fmt"
    "runtime"
    "time")func f() {
    for i := 0; ; i++ {
        fmt.Println(i)
        time.Sleep(10 * time.Millisecond)
    }}func main() {
    go f()
    runtime.Goexit()}

这可能比永远阻塞主函数更干净,特别是对于无限的程序。一个缺点是,如果进程返回或退出(包括主要的goroutine)的所有循环都会检测到这是一个错误和恐慌:

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

为了避免这种情况,至少必须有一个Goroutine调用os.Exit在它回来之前。呼叫os.Exit(0)立即终止程序,并表示它没有出错。例如:

package mainimport (
    "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()}


查看完整回答
反对 回复 2019-07-13
  • 3 回答
  • 0 关注
  • 1221 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信