2016-04-17 111 views
1

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

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 

这是OK:五次你好,五次世界。我开始变得奇怪,当我打电话

say("world") 
go say("hello") 

现在的输出就是

world 
world 
world 
world 
world 

的Hello任何责任。它带有两个办公室,甚至更加诡异

现在根本没有输出。当我改变i < 5i < 2并调用

go say("world") 
say("hello") 

我得到

world 
hello 
hello 

缺少什么我在这里?

+0

https://gobyexample.com/goroutines – elithrar

回答

5

say("world") 
go say("hello") 

“世界”的呼叫必须完成的“hello”够程前的情况下启动。 “hello”goroutine不运行或完成because main returns

对于

的够程不运行或由于主要收益齐全。

使用sync.WaitGroup以防止主从够程完成之前退出:

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

playground example

1

这是因为主函数已经退出。

当主函数返回时,所有的goroutines突然终止,然后程序退出。

您添加statment:

time.Sleep(100 * time.Second) 

主函数返回前,一切顺利。

但Go中的一个好习惯是使用通道,该通道用于在goroutine之间进行通信。你可以使用它让主函数等待后台程序完成。

0

随着

func main() { go say("world") say("hello") }

你正在创建两个单独的够程,一个是主功能goroutine和一个是去说(“世界”)。通常情况下,当函数被执行时,程序跳转到该函数,执行所有内部代码,然后在调用函数的位置后跳转到该行。

使用goroutines,你不是在函数内部跳转,而是在一个单独的线程中启动goroutine,并在调用之后继续执行该行,而不用等待它。

因此,在主办公室完成之前,goroutine没有时间完成。

2

恭喜学习Go。作为一个新人,理解并发性以及它与并行性有何不同是很好的。

并发
并发就像一个杂耍杂耍几个球在空气中,用一只手。无论他玩杂耍多少球,任何时候只有一个球触及他的手。

并行
当魔术师开始与并行另一只手耍弄更多的球,我们有两个并行过程在同一时间运行。

够程是伟大的,因为他们都是并发自动并行,这取决于可用的计算内核和GOMAXPROCS变量被设置。

单手变戏法
回到单手,单芯,同时变戏法。想象一下,他正在玩弄三个球,名字分别是“你好”,“世界”和“火星人”,手牌是main例程。

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

一旦三大球抛向空中顺序,变戏法只是闭关他的手的时候了。也就是说,在第一次投球之前,例程就会退出,甚至落在他手上。耻辱,球只是落在地上。糟糕的表演。

为了让球回到他的手中,杂耍者必须确保他为他们等待等待。这意味着他的手需要能够跟踪并统计他投掷的球,并在每次着陆时进行学习。

的最直接方式是使用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()减少计数器。一旦积压变为0,这意味着所有的goroutines都完成了,并且WaitGroup应该停止等待(并且抓住球!)。

虽然使用通道进行同步是很好的,但鼓励使用可用的并行工具,特别是在通道的使用使代码更复杂且难以理解的情况下。