Golang中使用"go"关键字和"runtime.Gosched()"时的控制流问题
Golang中使用"go"关键字和"runtime.Gosched()"时的控制流问题
package main
import (
"fmt"
"runtime"
"sync"
"sync/atomic"
)
func main() {
fmt.Println("NumCPU:", runtime.NumCPU())
fmt.Println("NumGoroutine: First", runtime.NumGoroutine())
var counter int64
const gs = 100
var wg sync.WaitGroup
wg.Add(gs)
for i := 0; i < gs; i++ {
go func() {
atomic.AddInt64(&counter, 1)
runtime.Gosched()
fmt.Println("Counter:\t", atomic.LoadInt64(&counter))
wg.Done()
}()
fmt.Println("NumGoroutine:", runtime.NumGoroutine())
}
wg.Wait()
fmt.Println("NumGoroutine: Last", runtime.NumGoroutine())
fmt.Println("count:", counter)
}
大家好,我是Go语言的新手。在学习Go的并发时,我遇到了这段代码,但无法理解其控制流程。我知道由于所有内容都是并行运行的,所以输出结果永远不会相同。
以下是我在终端/命令行中得到的输出:
NumCPU: 4 NumGoroutine: First 1 NumGoroutine: 2 Counter: 1 NumGoroutine: 2 Counter: 2 NumGoroutine: 2 Counter: 3 NumGoroutine: 2 Counter: 4 NumGoroutine: 3 Counter: 5 NumGoroutine: 2 Counter: 6 NumGoroutine: 2 Counter: 7 NumGoroutine: 3 Counter: 8 NumGoroutine: 3 Counter: 9 NumGoroutine: 3 Counter: 10 NumGoroutine: 3 Counter: 11 NumGoroutine: 3 Counter: 12 NumGoroutine: 3 Counter: 13 NumGoroutine: 3 Counter: 14 NumGoroutine: 3 Counter: 15 NumGoroutine: 3 Counter: 16 NumGoroutine: 3 Counter: 17 NumGoroutine: 3 Counter: 18 NumGoroutine: 3 Counter: 19 NumGoroutine: 3 Counter: 20 NumGoroutine: 3 Counter: 21 NumGoroutine: 3 Counter: 22 NumGoroutine: 3 Counter: 23 NumGoroutine: 3 Counter: 24 NumGoroutine: 3 Counter: 25 NumGoroutine: 3 Counter: 26 NumGoroutine: 3 Counter: 27 NumGoroutine: 3 Counter: 28 NumGoroutine: 3 Counter: 29 NumGoroutine: 3 Counter: 30 NumGoroutine: 3 Counter: 31 NumGoroutine: 3 Counter: 32 NumGoroutine: 3 Counter: 33 NumGoroutine: 3 Counter: 34 NumGoroutine: 3 Counter: 35 NumGoroutine: 3 Counter: 36 NumGoroutine: 3 Counter: 37 NumGoroutine: 3 Counter: 38 NumGoroutine: 3 NumGoroutine: 3 Counter: 39 Counter: 40 NumGoroutine: 4 Counter: 41 NumGoroutine: 3 Counter: 42 NumGoroutine: 3 Counter: 43 NumGoroutine: 3 Counter: 44 NumGoroutine: 3 Counter: 45 NumGoroutine: 3 Counter: 46 NumGoroutine: 3 Counter: 47 NumGoroutine: 3 NumGoroutine: 3 Counter: 48 Counter: 49 NumGoroutine: 4 Counter: 50 NumGoroutine: 3 Counter: 51 NumGoroutine: 3 Counter: 52 NumGoroutine: 3 Counter: 53 NumGoroutine: 3 Counter: 54 NumGoroutine: 3 NumGoroutine: 3 Counter: 55 Counter: 56 NumGoroutine: 4 Counter: 57 NumGoroutine: 3 Counter: 58 NumGoroutine: 3 Counter: 59 NumGoroutine: 3 Counter: 60 NumGoroutine: 3 NumGoroutine: 3 Counter: 61 Counter: 62 NumGoroutine: 4 Counter: 63 NumGoroutine: 3 NumGoroutine: 3 Counter: 64 Counter: 65 NumGoroutine: 4 Counter: 66 NumGoroutine: 3 Counter: 67 NumGoroutine: 3 Counter: 68 NumGoroutine: 3 Counter: 69 NumGoroutine: 3 Counter: 70 NumGoroutine: 3 Counter: 71 NumGoroutine: 3 Counter: 72 NumGoroutine: 3 Counter: 73 NumGoroutine: 3 Counter: 74 NumGoroutine: 3 Counter: 75 NumGoroutine: 3 Counter: 76 NumGoroutine: 3 Counter: 77 NumGoroutine: 3 Counter: 78 NumGoroutine: 3 Counter: 79 NumGoroutine: 3 Counter: 80 NumGoroutine: 3 Counter: 81 NumGoroutine: 3 Counter: 82 NumGoroutine: 3 Counter: 83 NumGoroutine: 3 Counter: 84 NumGoroutine: 3 Counter: 85 NumGoroutine: 3 Counter: 86 NumGoroutine: 3 Counter: 87 NumGoroutine: 3 Counter: 88 NumGoroutine: 3 Counter: 89 NumGoroutine: 3 Counter: 90 NumGoroutine: 3 Counter: 91 NumGoroutine: 3 Counter: 92 NumGoroutine: 3 Counter: 93 NumGoroutine: 3 Counter: 94 NumGoroutine: 3 Counter: 95 NumGoroutine: 3 Counter: 96 NumGoroutine: 3 Counter: 97 NumGoroutine: 3 Counter: 98 NumGoroutine: 3 Counter: 99 NumGoroutine: 3 Counter: 100 NumGoroutine: Last 1 count: 100
当程序第一次执行到 runtime.Gosched() 这一行时会发生什么?控制流程会转向哪里?
为什么在输出的第三行,NumGoroutine 会在 count 之前打印出来?
更多关于Golang中使用"go"关键字和"runtime.Gosched()"时的控制流问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
感谢您付出的时间和努力 Jeff Emanuel。 
更多关于Golang中使用"go"关键字和"runtime.Gosched()"时的控制流问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
为什么在输出的第三行,
NumGoroutine会先于count打印出来?
因为在打印 “NumGoroutine” 的那行代码执行之前,循环中的 goroutine 还没有开始执行。goroutine 是异步运行的。如果没有显式的同步机制,你无法控制它相对于其他 goroutine 的运行时机。
这段代码展示了Go调度器的工作机制。关键点在于runtime.Gosched()和goroutine调度之间的交互。
当程序执行到runtime.Gosched()时,当前goroutine会主动让出处理器,允许其他goroutine运行。控制流程会转向Go调度器,由调度器决定接下来运行哪个goroutine。
关于输出顺序的问题,这里有一个更清晰的示例来说明调度顺序:
package main
import (
"fmt"
"runtime"
"sync"
"sync/atomic"
)
func main() {
fmt.Println("NumCPU:", runtime.NumCPU())
fmt.Println("NumGoroutine: First", runtime.NumGoroutine())
var counter int64
const gs = 5 // 减少数量以便观察
var wg sync.WaitGroup
wg.Add(gs)
for i := 0; i < gs; i++ {
go func(id int) {
fmt.Printf("Goroutine %d: before atomic.Add\n", id)
atomic.AddInt64(&counter, 1)
fmt.Printf("Goroutine %d: before Gosched\n", id)
runtime.Gosched()
fmt.Printf("Goroutine %d: Counter = %d\n", id, atomic.LoadInt64(&counter))
wg.Done()
}(i)
fmt.Printf("Main: after launching goroutine %d, NumGoroutine = %d\n",
i, runtime.NumGoroutine())
}
wg.Wait()
fmt.Println("NumGoroutine: Last", runtime.NumGoroutine())
fmt.Println("count:", counter)
}
输出可能类似这样:
NumCPU: 4
NumGoroutine: First 1
Main: after launching goroutine 0, NumGoroutine = 2
Main: after launching goroutine 1, NumGoroutine = 3
Goroutine 0: before atomic.Add
Goroutine 0: before Gosched
Main: after launching goroutine 2, NumGoroutine = 4
Goroutine 1: before atomic.Add
Goroutine 1: before Gosched
Goroutine 0: Counter = 2
Main: after launching goroutine 3, NumGoroutine = 4
Goroutine 2: before atomic.Add
Goroutine 2: before Gosched
Goroutine 1: Counter = 3
Main: after launching goroutine 4, NumGoroutine = 5
Goroutine 3: before atomic.Add
Goroutine 3: before Gosched
Goroutine 2: Counter = 4
Goroutine 4: before atomic.Add
Goroutine 4: before Gosched
Goroutine 3: Counter = 5
Goroutine 4: Counter = 5
NumGoroutine: Last 1
count: 5
在你的原始代码中,NumGoroutine在count之前打印是因为:
- 主goroutine在循环中启动新的goroutine后立即执行
fmt.Println("NumGoroutine:", runtime.NumGoroutine()) - 新启动的goroutine执行
atomic.AddInt64(&counter, 1)然后调用runtime.Gosched() runtime.Gosched()让出处理器后,控制权可能返回到主goroutine,继续打印NumGoroutine- 或者另一个goroutine获得处理器,打印
Counter值
这种交错输出是由于goroutine的并发执行和调度器的非确定性调度造成的。每次运行都可能产生不同的输出顺序,这正是并发编程的特点。

