Golang中为什么下层的goroutine会先执行?
Golang中为什么下层的goroutine会先执行?
package main
import (
"fmt"
"sync"
"time"
)
func odd(ch chan int, wg *sync.WaitGroup) {
for i := 1; ; i += 2 {
<-ch
fmt.Println(i)
time.Sleep(time.Second)
ch <- 1
}
wg.Done()
}
func even(ch chan int, wg *sync.WaitGroup) {
for i := 2; ; i += 2 {
<-ch
fmt.Println(i)
time.Sleep(time.Second)
ch <- 1
}
wg.Done()
}
func main() {
ch := make(chan int)
var wg sync.WaitGroup
wg.Add(2)
go even(ch, &wg)
go odd(ch, &wg)
ch <- 1
wg.Wait()
}
我想知道为什么名为 odd 的函数会先执行,然后才是名为 even 的函数?我是不是遗漏了一些非常基础的东西?
更多关于Golang中为什么下层的goroutine会先执行?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
两个 Go 协程之间没有确定的执行顺序。
观察到的结果可能会因编译所用的 Go 版本、CPU/核心数量、环境条件以及随机性而改变。
MukulLatiyan:
package main
import (
"fmt"
"sync"
"time"
)
func odd(ch chan int, wg *sync.WaitGroup) {
for i := 1; i < 5; i += 2 {
<-ch
fmt.Println("odd", i)
time.Sleep(time.Second)
ch <- 1
}
wg.Done()
}
func even(ch chan int, wg *sync.WaitGroup) {
for i := 2; i < 5; i += 2 {
<-ch
fmt.Println("even", i)
time.Sleep(time.Second)
ch <- 1
}
wg.Done()
}
func main() {
ch := make(chan int)
var wg sync.WaitGroup
wg.Add(2)
go even(ch, &wg)
go odd(ch, &wg)
ch <- 1
time.Sleep(3 * time.Second)
<-ch
wg.Wait()
}
看看这个稍微修改过的程序版本。它的优点是这段代码运行后会结束。我在 Go Playground 上运行了 10 次,其中有一次它先打印了 “even 2”,其他情况下都是 “odd 2”。
决定哪个 goroutine 先运行的因素,是这两个 goroutine 在运行时的调度方式。第一个 goroutine 比第二个有微小的优势,因为主函数先启动它,然后才启动第二个。
一旦两个 goroutine 中的任何一个开始运行,由于它会向通道写入数据,而另一个 goroutine 在等待从同一个通道读取数据,所以顺序就被固定下来了。
请不要再抨击原帖作者了,他的问题即使表述不佳,也是合理的。
在Golang中,goroutine的执行顺序是不确定的,由调度器决定。但在你的代码中,odd函数先执行的原因是通道通信的同步机制和goroutine启动的时序共同作用的结果。
具体分析如下:
-
通道的同步特性:无缓冲通道
ch的发送和接收操作会阻塞,直到另一端准备好。当ch <- 1执行时,会等待有goroutine执行<-ch来接收数据。 -
goroutine启动顺序:虽然
go even(ch, &wg)先被调用,但goroutine的启动需要时间。实际执行时,可能是odd的<-ch先准备好接收数据。 -
代码执行流程:
func main() { ch := make(chan int) // 创建无缓冲通道 var wg sync.WaitGroup wg.Add(2) go even(ch, &wg) // 启动even goroutine(可能还未开始执行循环) go odd(ch, &wg) // 启动odd goroutine(可能还未开始执行循环) ch <- 1 // 主goroutine发送数据,阻塞等待接收者 // 此时哪个goroutine先执行到 <-ch,哪个就先接收数据 wg.Wait() } -
实际执行示例:
// 添加调试信息来观察执行顺序 package main import ( "fmt" "sync" "time" ) func odd(ch chan int, wg *sync.WaitGroup, id string) { fmt.Printf("%s: started\n", id) for i := 1; ; i += 2 { fmt.Printf("%s: waiting to receive\n", id) <-ch fmt.Printf("%s: received, printing %d\n", id, i) time.Sleep(time.Second) ch <- 1 } wg.Done() } func main() { ch := make(chan int) var wg sync.WaitGroup wg.Add(2) go odd(ch, &wg, "odd") go odd(ch, &wg, "even") // 使用相同函数便于观察 time.Sleep(100 * time.Millisecond) // 给goroutine启动时间 fmt.Println("main: sending initial value") ch <- 1 wg.Wait() } -
控制执行顺序的方法:如果需要特定的执行顺序,可以使用同步原语:
package main import ( "fmt" "sync" "time" ) func main() { ch := make(chan int) var wg sync.WaitGroup var mu sync.Mutex mu.Lock() // 先锁住even wg.Add(2) go func() { mu.Lock() // even等待锁 defer mu.Unlock() for i := 2; ; i += 2 { <-ch fmt.Println(i) time.Sleep(time.Second) ch <- 1 } wg.Done() }() go func() { for i := 1; ; i += 2 { <-ch fmt.Println(i) time.Sleep(time.Second) ch <- 1 } wg.Done() }() ch <- 1 mu.Unlock() // 释放锁,even可以执行 wg.Wait() }
在你的原始代码中,odd先执行是因为调度器让odd的<-ch先于even的<-ch执行。这种顺序不是固定的,多次运行可能会得到不同的结果。这是Go并发模型的设计特点:goroutine的执行顺序是非确定性的。

