繁体   English   中英

没有来自 goroutine 的 output

[英]No output from goroutine

SayHello()按预期执行时,goroutine 什么也不打印。

package main

import "fmt"

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

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

当你的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()函数随机打印“混合”数字:您无法保证观察到这种行为。 同样,请参阅上述答案以获取更多详细信息。 Go 内存模型仅保证某些事件在其他事件之前发生,您无法保证 2 个并发 goroutines 是如何执行的。

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

runtime.GOMAXPROCS(2)

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

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

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

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

一种选择是使用诸如sync.WaitGroup之类的东西来等待main产生的其他 goroutine,然后再从main返回。

另一种选择是在main中调用runtime.Goexit() godoc

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

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

这允许主 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()
}

这比在主函数中永远阻塞更干净,尤其是对于无限的程序。 一个缺点是,如果一个进程的所有 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()
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM