请考虑此代码段
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")
}
Run Code Online (Sandbox Code Playgroud)
运行此类代码可获得预期输出:
nap time
nap done
nap done
nap done
all done
Run Code Online (Sandbox Code Playgroud)
现在让我们先省略第一个标准输出打印wg.Wait():
// fmt.Println("nap time")
wg.Wait()
fmt.Println("all done")
Run Code Online (Sandbox Code Playgroud)
输出现在变为意外:
all done
Run Code Online (Sandbox Code Playgroud)
预期的地方是:
nap done
nap done
nap done
all done
Run Code Online (Sandbox Code Playgroud)
操场上的相同代码确实提供了此输出,而无需省略stdout打印.
你能解释一下,我在那里失踪了吗?
虽然这看起来像魔术,但它有一个合乎逻辑的解释.Go不保证goroutines执行的顺序.在给定的代码段代码中有三个goroutine,但实际上有四个:执行开始时产生的第一个.
这个goroutine产生了三个小睡功能并继续其计划.它是如此之快,以至于它wg.Wait()在任何产生的goroutine能够调用之前执行wg.Add(1).结果wg.Wait()没有阻止执行和程序结束.
wg.Wait()在这种情况下,程序执行是不同的,goroutines能够进行 wg.Add(1)调用,因为主要的goroutine并不像第一种情况那样快.此行为无法保证,可以在链接的游乐场示例中看到.
以下代码示例将提供相同的预期输出:
time.Sleep(time.Second)
wg.Wait()
fmt.Println("all done")
Run Code Online (Sandbox Code Playgroud)
与此fmt.Println()产生的影响相同time.Sleep().
规则很简单:wg.Add(1)在产生goroutine之前调用.
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")
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
54 次 |
| 最近记录: |