试着去理解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()
}