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

goroutine 没有输出

goroutine 没有输出

Go
慕标琳琳 2021-09-20 20:33:40
当SayHello()按预期执行时,goroutine 不打印任何内容。package mainimport "fmt"func SayHello() {    for i := 0; i < 10 ; i++ {        fmt.Print(i, " ")    }}func main() {    SayHello()    go SayHello()}
查看完整描述

3 回答

?
慕标5832272

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

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


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


程序执行首先初始化主包,然后调用函数main。当该函数调用返回时,程序退出。它不会等待其他(非main)goroutine 完成。


有关更多详细信息,请参阅此答案。


你必须告诉你的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

}

笔记:


根据您的编辑/评论:如果您希望 2 个正在运行的SayHello()函数随机打印“混合”数字:您不能保证观察到这种行为。再次,有关更多详细信息,请参阅上述答案。在转到内存模型只能保证某些事件发生的其他事件之前,你有没有保证2个并发够程是如何执行的。


您可能会尝试使用它,但要知道结果不是确定性的。首先,您必须启用多个活动 goroutines 才能执行:


runtime.GOMAXPROCS(2)

其次,你必须首先SayHello()作为一个 goroutine启动,因为你当前的代码首先SayHello()在主 goroutine 中执行,只有在它完成后才会启动另一个:


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


查看完整回答
反对 回复 2021-09-20
?
肥皂起泡泡

TA贡献1829条经验 获得超6个赞

或者(对于 icza 的回答)您可以使用WaitGroupfromsync包和匿名函数来避免更改原始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()

}

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


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

}


查看完整回答
反对 回复 2021-09-20
?
MM们

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

当main函数返回时,Go 程序退出。


一种选择是使用类似的方法sync.WaitGroup来等待main从main.


另一种方法是调用runtime.Goexit()在main。从godoc:


Goexit 终止调用它的 goroutine。没有其他 goroutine 受到影响。Goexit 在终止 goroutine 之前运行所有延迟调用。因为 Goexit 不是一个恐慌,这些延迟函数中的任何恢复调用都将返回 nil。


从主协程调用 Goexit 会终止该协程,而不会返回 func main。由于 func main 没有返回,程序继续执行其他 goroutine。如果所有其他 goroutine 退出,程序就会崩溃。


这允许 main goroutine 停止执行,而后台例程继续执行。例如:


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

}

这比在 main 函数中永远阻塞更干净,尤其是对于无限的程序。一个缺点是,如果进程的所有 goroutine 都返回或退出(包括主 goroutine),Go 会将其检测为错误和恐慌:


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

为了避免这种情况,至少一个 goroutine 必须os.Exit在它返回之前调用。调用os.Exit(0)立即终止程序并指示它没有错误地这样做。例如:


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

}

查看完整回答
反对 回复 2021-09-20
  • 3 回答
  • 0 关注
  • 377 浏览
慕课专栏
更多

添加回答

举报

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