Go sync.Waitgroup不';不要阻止执行
考虑一下这个代码片段Go sync.Waitgroup不';不要阻止执行,go,Go,考虑一下这个代码片段 package main import ( "fmt" "sync" "time" ) func main() { wg := new(sync.WaitGroup) nap := func() { wg.Add(1) time.Sleep(2 * time.Second) fmt.Println("nap done") wg.Done() } go
package main
import (
"fmt"
"sync"
"time"
)
func main() {
wg := new(sync.WaitGroup)
nap := func() {
wg.Add(1)
time.Sleep(2 * time.Second)
fmt.Println("nap done")
wg.Done()
}
go nap()
go nap()
go nap()
fmt.Println("nap time")
wg.Wait()
fmt.Println("all done")
}
运行这样的代码会产生预期的输出:
nap time
nap done
nap done
nap done
all done
time.Sleep(time.Second)
wg.Wait()
fmt.Println("all done")
现在,让我们在wg.Wait()之前打印第一个标准输出:
输出现在更改为意外值:
all done
在预期的情况下:
nap done
nap done
nap done
all done
上的相同代码确实提供了此输出,而无需省略标准输出
你能给我解释一下,我遗漏了什么吗?尽管这看起来很神奇,但它有一个合乎逻辑的解释。Go不能保证goroutines的执行顺序。在给定的代码片段中产生了三个goroutine,但实际上有四个:第一个是在执行开始时产生的goroutine
省略标准打印
这个goroutine产生了三个nap函数,并在其计划中继续。它的速度如此之快,以至于在任何生成的goroutine能够调用wg.Add(1)
之前,它执行了wg.Wait()
。因此,wg.Wait()
没有阻止执行,程序结束
在wg.Wait()之前打印到标准输出
在这种情况下,程序的执行是不同的,goroutine能够进行wg.Add(1)
调用,因为主goroutine不像第一种情况那样快。这种行为不能保证,这可以在链接的游乐场示例中看到
这与标准打印无关
以下代码示例将提供相同的预期输出:
nap time
nap done
nap done
nap done
all done
time.Sleep(time.Second)
wg.Wait()
fmt.Println("all done")
fmt.Println()
惯用方式
规则很简单:在生成goroutine之前调用wg.Add(1)
package main
import (
"fmt"
"sync"
"time"
)
func main() {
wg := new(sync.WaitGroup)
nap := func() {
time.Sleep(2 * time.Second)
fmt.Println("nap done")
wg.Done()
}
napCount := 3
wg.Add(napCount)
for i := 0; i < napCount; i++ {
go nap()
}
wg.Wait()
fmt.Println("all done")
}
主程序包
进口(
“fmt”
“同步”
“时间”
)
func main(){
wg:=新建(sync.WaitGroup)
nap:=func(){
时间。睡眠(2*时间。秒)
fmt.Println(“nap完成”)
wg.Done()
}
napCount:=3
工作组添加(napCount)
对于i:=0;i
尽管这看起来很神奇,但它有一个合乎逻辑的解释。Go不能保证goroutines的执行顺序。在给定的代码片段中产生了三个goroutine,但实际上有四个:第一个是在执行开始时产生的goroutine
省略标准打印
这个goroutine产生了三个nap函数,并在其计划中继续。它的速度如此之快,以至于在任何生成的goroutine能够调用wg.Add(1)
之前,它执行了wg.Wait()
。因此,wg.Wait()
没有阻止执行,程序结束
在wg.Wait()之前打印到标准输出
在这种情况下,程序的执行是不同的,goroutine能够进行wg.Add(1)
调用,因为主goroutine不像第一种情况那样快。这种行为不能保证,这可以在链接的游乐场示例中看到
这与标准打印无关
以下代码示例将提供相同的预期输出:
nap time
nap done
nap done
nap done
all done
time.Sleep(time.Second)
wg.Wait()
fmt.Println("all done")
fmt.Println()
惯用方式
规则很简单:在生成goroutine之前调用wg.Add(1)
package main
import (
"fmt"
"sync"
"time"
)
func main() {
wg := new(sync.WaitGroup)
nap := func() {
time.Sleep(2 * time.Second)
fmt.Println("nap done")
wg.Done()
}
napCount := 3
wg.Add(napCount)
for i := 0; i < napCount; i++ {
go nap()
}
wg.Wait()
fmt.Println("all done")
}
主程序包
进口(
“fmt”
“同步”
“时间”
)
func main(){
wg:=新建(sync.WaitGroup)
nap:=func(){
时间。睡眠(2*时间。秒)
fmt.Println(“nap完成”)
wg.Done()
}
napCount:=3
工作组添加(napCount)
对于i:=0;i
这是一个很好的答案,但可以通过解释OP如何正确地等待goroutine完成而不退出调用goroutine(以及Go中处理数据共享的惯用方法)来做得更好。虽然这是一个很好的信息,但我在任何地方都看不到实际的答案,也就是说,在发送goroutine之前,始终要调用wg.Add
。是的,如果他在启动goroutine之前调用Add,这会解决问题。感谢您的宝贵意见,我已经更新了答案。这是一个很好的答案,但可以通过解释OP如何正确地等待goroutine完成而不退出调用goroutine(以及Go中处理数据共享的惯用方法)来做得更好。虽然这是一个很好的信息,但我在任何地方都看不到实际的答案,也就是说,在发送goroutine之前,总是要调用wg.Add
。是的,如果他在启动goroutine之前调用Add,这会解决问题。谢谢你的宝贵意见,我已经更新了答案。