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

试图理解 goroutines

试图理解 goroutines

Go
白衣非少年 2022-01-04 15:53:45
我一直在玩A Tour of Go 中的以下代码,但我不明白当我应用一些小的更改时发生了什么。原代码是这样的package mainimport (    "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")}它产生了这个worldhellohelloworldworldhellohelloworldworldhello没关系:五次你好,五次世界。当我打电话时我开始变得奇怪say("world")go say("hello")现在输出只是worldworldworldworldworld没有你好。有两个 goroutines 就更奇怪了go say("world")go say("hello")现在根本没有输出。当我改变i < 5到i < 2和呼叫go say("world")say("hello")我得到worldhellohello我在这里错过了什么?
查看完整描述

3 回答

?
POPMUISE

TA贡献1765条经验 获得超5个赞

如果是


 say("world")

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

  defer wg.Done()

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

    time.Sleep(100 * time.Millisecond)

    fmt.Println(s)

  }

}


func main() {

  var wg sync.WaitGroup

  wg.Add(2)

  go say(&wg, "world")

  go say(&wg, "hello")

  wg.Wait()

}


查看完整回答
反对 回复 2022-01-04
?
心有法竹

TA贡献1866条经验 获得超5个赞

恭喜你学习了 Go。作为一个新手,理解并发以及它与并行的不同是很好的。


并发

并发就像一个杂耍者用一只手在空中玩弄几个球。无论他在玩多少个球,任何时候都只有一个球碰到他的手。


并行性

当杂耍者开始用另一只手并行处理更多的球时,我们有两个并发进程同时运行。


Goroutines 很棒,因为它们既是并发又是自动并行,这取决于可用的计算核心和GOMAXPROCS设置的变量。


单手杂耍者

回到单手、单核、并发杂耍者。想象一下,他以手为main套路,分别玩弄三个分别名为“hello”、“world”和“mars”的球。


var balls = []string{"hello", "world", "mars"}


func main() {

        go say(balls[0])

        go say(balls[1])

        go say(balls[2])

}

或者更恰当地说,


func main() {

        for _, ball := range balls {

                go say(ball)

        }

}

一旦三个球依次抛向空中,杂耍者只需立即收回手。也就是说,在main投出的第一个球甚至可以落在他的手上之前,例程就退出了。耻辱,球只是掉到了地上。糟糕的表演。


为了让球回到他的手中,杂耍者必须确保他等待它们。这意味着他的手需要能够跟踪和计算他投出的球,并在每个球落地时学习。


最直接的方法是使用sync.WaitGroup:


import (

    "fmt"

    "time"

    "sync"

)


var balls = []string{"hello", "world", "mars"}

var wg sync.WaitGroup


func main() {

        for _, ball := range balls {

                // One ball thrown

                wg.Add(1)

                go func(b string) {

                        // Signals the group that this routine is done.

                        defer wg.Done()

                        // each ball hovers for 1 second

                        time.Sleep(time.Duration(1) * time.Second)

                        fmt.Println(b)

                        // wg.Done() is called before goroutine exits

                }(ball)

        }


        // The juggler can do whatever he pleases while the 

        // balls are hanging in the air.


        // This hand will come back to grab the balls after 1s.

        wg.Wait()

}

WaitGroup很简单。当一个 goroutine 被生成时,一个人添加到一个“积压计数器”WaitGroup.Add(1)并调用WaitGroup.Done()以减少计数器。一旦 backlog 变为 0,就意味着所有 goroutine 都完成了,WaitGroup应该停止等待(并抓住球!)。


虽然使用通道进行同步很好,但鼓励使用可用的并发工具,尤其是当使用通道使代码更加复杂和难以理解时。


查看完整回答
反对 回复 2022-01-04
?
一只萌萌小番薯

TA贡献1795条经验 获得超7个赞

那是因为main函数已经退出了。

当 main 函数返回时,所有 goroutine 都会突然终止,然后程序退出。

你添加一个语句:

time.Sleep(100 * time.Second)

在 main 函数返回之前,一切顺利。

但是在 Go 中一个好的做法是使用 channel,它用于在 goroutine 之间进行通信。您可以使用它让 main 函数等待后台 goroutines 完成。


查看完整回答
反对 回复 2022-01-04
  • 3 回答
  • 0 关注
  • 247 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号