试着去理解goroutines

试着去理解goroutines,go,goroutine,Go,Goroutine,我一直在使用来自的以下代码,但我不明白在应用一些小更改时会发生什么。原始代码是这样的 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"

我一直在使用来自的以下代码,但我不明白在应用一些小更改时会发生什么。原始代码是这样的

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")
}
哪一个是好的:五次你好,五次世界。我打电话时开始感到奇怪

say("world")
go say("hello")
现在输出刚刚好

world
world
world
world
world
不,不管怎样。更奇怪的是有两次出游

go say("world")
go say("hello")
现在根本没有输出。当我将
I<5
更改为
I<2
并调用

go say("world")
say("hello")
我明白了

world
hello
hello

我在这里遗漏了什么?

这是因为主功能已退出

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

您可以添加一个语句:

time.Sleep(100 * time.Second)
在主函数返回之前,一切都很顺利

但Go中的一个良好实践是使用通道,它用于在Goroutine之间进行通信。您可以使用它让主函数等待后台goroutines完成

在以下情况下:

 say("world")
 go say("hello")
“世界”呼叫必须在“hello”goroutine启动之前完成。“hello”goroutine未运行或完成

为了

goroutines不会运行或完成,因为main返回

用于在goroutines完成之前防止main退出:

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()
}
func-say(wg*sync.WaitGroup,s字符串){
推迟工作组完成()
对于i:=0;i<5;i++{
时间。睡眠(100*时间。毫秒)
fmt.Println(s)
}
}
func main(){
var wg sync.WaitGroup
工作组.添加(2)
去说(&wg,“世界”)
去说(&wg,“你好”)
wg.Wait()
}
带有

func main(){
去说(“世界”)
说(“你好”)
}

您正在创建两个独立的goroutine,一个是goroutine的主要功能,另一个是go-say(“世界”)。通常,当执行函数时,程序跳转到该函数,执行其中的所有代码,然后跳转到调用该函数后的行

使用goroutine,您不会在函数内部跳转,而是在单独的线程中启动goroutine,并在调用后继续执行该行,而不必等待


因此,在完成主goroutine之前,goroutine将没有时间完成

祝贺您学习围棋。作为一个新手,理解并发性以及它与并行性的区别是很好的

并发性
并发性就像一个玩杂耍的人用一只手在空中玩几个球。不管他玩了多少球,在任何时候都只有一个球碰到他的手

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

Goroutines非常好,因为它们是并发的和自动并行的,这取决于可用的计算核心和设置的
GOMAXPROCS
变量

单手魔术师
回到单手,单核心,同时杂耍。想象一下,他在玩三个分别名为“你好”、“世界”和“火星”的球,手是
主要的
程序

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
例程在投出的第一个球落在他的手上之前退出。可惜,球掉到地上了。糟糕的表演

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

最直接的方法是使用:

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

虽然使用通道进行同步是可以的,但鼓励在适当的情况下使用可用的并发工具,特别是当通道的使用使代码更加复杂和难以理解时

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