Golang协程使用中遇到的意外输出问题
Golang协程使用中遇到的意外输出问题
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func main() {
wg.Add(2)
go func() {
fmt.Println("Hello form one")
wg.Done()
}()
go func() {
fmt.Println("Hello form two")
wg.Done()
}()
wg.Wait()
}
运行上述程序。 为什么先打印出“Hello form two”,然后才是“Hello from one”。
更多关于Golang协程使用中遇到的意外输出问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
3 回复
“go” 语句启动一个函数调用,使其作为一个独立的并发控制线程(或称 goroutine)在同一个地址空间内执行。
Go 运行时调度器以伪随机、公平的方式并发地运行 goroutine。
既然你无法预测一个公平骰子的投掷结果,你就必须尝试"操纵骰子"。例如:
package main
import (
"fmt"
"runtime"
"sync"
)
var wg sync.WaitGroup
func main() {
runtime.GOMAXPROCS(1)
wg.Add(2)
go func() {
fmt.Println("Hello form one")
wg.Done()
}()
runtime.Gosched()
go func() {
fmt.Println("Hello form two")
wg.Done()
}()
wg.Wait()
}
Hello form one
Hello form two
这是一个典型的并发执行顺序问题。在Go语言中,goroutine的调度是非确定性的,执行顺序由Go运行时调度器决定。
核心原因:
- 两个goroutine是并发执行的,没有同步机制保证执行顺序
- Go调度器根据系统状态决定哪个goroutine先获得CPU时间片
- 每次运行都可能得到不同的输出顺序
示例代码证明:
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
// 运行多次观察不同结果
for i := 0; i < 5; i++ {
wg.Add(2)
go func(id int) {
fmt.Printf("Iteration %d: Hello from goroutine %d\n", i, id)
wg.Done()
}(1)
go func(id int) {
fmt.Printf("Iteration %d: Hello from goroutine %d\n", i, id)
wg.Done()
}(2)
wg.Wait()
fmt.Println("---")
time.Sleep(time.Millisecond * 10) // 增加调度变化
}
}
输出可能类似:
Iteration 5: Hello from goroutine 2
Iteration 5: Hello from goroutine 1
---
Iteration 5: Hello from goroutine 1
Iteration 5: Hello from goroutine 2
---
Iteration 5: Hello from goroutine 2
Iteration 5: Hello from goroutine 1
---
如果需要固定顺序,必须使用同步机制:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func main() {
wg.Add(2)
ch := make(chan struct{}) // 同步通道
go func() {
<-ch // 等待信号
fmt.Println("Hello form one")
wg.Done()
}()
go func() {
fmt.Println("Hello form two")
close(ch) // 发送信号
wg.Done()
}()
wg.Wait()
}
在这个修改版本中,第二个goroutine会先执行,第一个goroutine会等待通道信号,从而保证"Hello form two"总是先打印。


