试图理解 goroutines

我一直在玩A Tour of Go 中的以下代码,但我不明白当我应用一些小的更改时发生了什么。原代码是这样的


package main


import (

    "fmt"

    "time"

)


func say(s string) {

    for i := 0; i < 5; i++ {

        time.Sleep(100 * time.Millisecond)

        fmt.Println(s)

    }

}


func main() {

    go say("world")

    say("hello")

}

它产生了这个


world

hello

hello

world

world

hello

hello

world

world

hello

没关系:五次你好,五次世界。当我打电话时我开始变得奇怪


say("world")

go say("hello")

现在输出只是


world

world

world

world

world

没有你好。有两个 goroutines 就更奇怪了


go say("world")

go say("hello")

现在根本没有输出。当我改变i < 5到i < 2和呼叫


go say("world")

say("hello")

我得到


world

hello

hello

我在这里错过了什么?


白衣非少年
浏览 248回答 3
3回答

POPMUISE

如果是&nbsp;say("world")&nbsp;go say("hello")“world”调用必须在“hello” goroutine 启动之前完成。“hello” goroutine 没有运行或完成,因为 main 返回了。为了go say("world")go say("hello")goroutine 不会运行或完成,因为 main 返回。使用sync.WaitGroup防止 main 在 goroutines 完成之前退出:func say(wg *sync.WaitGroup, s string) {&nbsp; defer wg.Done()&nbsp; for i := 0; i < 5; i++ {&nbsp; &nbsp; time.Sleep(100 * time.Millisecond)&nbsp; &nbsp; fmt.Println(s)&nbsp; }}func main() {&nbsp; var wg sync.WaitGroup&nbsp; wg.Add(2)&nbsp; go say(&wg, "world")&nbsp; go say(&wg, "hello")&nbsp; wg.Wait()}

心有法竹

恭喜你学习了 Go。作为一个新手,理解并发以及它与并行的不同是很好的。并发并发就像一个杂耍者用一只手在空中玩弄几个球。无论他在玩多少个球,任何时候都只有一个球碰到他的手。并行性当杂耍者开始用另一只手并行处理更多的球时,我们有两个并发进程同时运行。Goroutines 很棒,因为它们既是并发又是自动并行,这取决于可用的计算核心和GOMAXPROCS设置的变量。单手杂耍者回到单手、单核、并发杂耍者。想象一下,他以手为main套路,分别玩弄三个分别名为“hello”、“world”和“mars”的球。var balls = []string{"hello", "world", "mars"}func main() {&nbsp; &nbsp; &nbsp; &nbsp; go say(balls[0])&nbsp; &nbsp; &nbsp; &nbsp; go say(balls[1])&nbsp; &nbsp; &nbsp; &nbsp; go say(balls[2])}或者更恰当地说,func main() {&nbsp; &nbsp; &nbsp; &nbsp; for _, ball := range balls {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; go say(ball)&nbsp; &nbsp; &nbsp; &nbsp; }}一旦三个球依次抛向空中,杂耍者只需立即收回手。也就是说,在main投出的第一个球甚至可以落在他的手上之前,例程就退出了。耻辱,球只是掉到了地上。糟糕的表演。为了让球回到他的手中,杂耍者必须确保他等待它们。这意味着他的手需要能够跟踪和计算他投出的球,并在每个球落地时学习。最直接的方法是使用sync.WaitGroup:import (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "time"&nbsp; &nbsp; "sync")var balls = []string{"hello", "world", "mars"}var wg sync.WaitGroupfunc main() {&nbsp; &nbsp; &nbsp; &nbsp; for _, ball := range balls {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // One ball thrown&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; go func(b string) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Signals the group that this routine is done.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; defer wg.Done()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // each ball hovers for 1 second&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(time.Duration(1) * time.Second)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(b)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // wg.Done() is called before goroutine exits&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }(ball)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; // The juggler can do whatever he pleases while the&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; // balls are hanging in the air.&nbsp; &nbsp; &nbsp; &nbsp; // This hand will come back to grab the balls after 1s.&nbsp; &nbsp; &nbsp; &nbsp; wg.Wait()}WaitGroup很简单。当一个 goroutine 被生成时,一个人添加到一个“积压计数器”WaitGroup.Add(1)并调用WaitGroup.Done()以减少计数器。一旦 backlog 变为 0,就意味着所有 goroutine 都完成了,WaitGroup应该停止等待(并抓住球!)。虽然使用通道进行同步很好,但鼓励使用可用的并发工具,尤其是当使用通道使代码更加复杂和难以理解时。

一只萌萌小番薯

那是因为main函数已经退出了。当 main 函数返回时,所有 goroutine 都会突然终止,然后程序退出。你添加一个语句:time.Sleep(100&nbsp;*&nbsp;time.Second)在 main 函数返回之前,一切顺利。但是在 Go 中一个好的做法是使用 channel,它用于在 goroutine 之间进行通信。您可以使用它让 main 函数等待后台 goroutines 完成。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go