我正在玩Go,在我的2015年8核MacBookPro上运行1.7.3。
试图理解当runtime.GOMAXPROCS设置为它的最大值(256)时调度程序如何工作,并且启动相同数量的goroutine,每个都运行一个无限循环。
我的假设是运行时会产生runtime.GOMAXPROCS操作系统线程数(即256个线程)并在这些线程中运行我的goroutine。
我希望以下代码可以打印256 1' s:
func main() {
procs := 256
runtime.GOMAXPROCS(procs)
for i := 0; i < procs; i++ {
go func() {
fmt.Print("1")
for {}
}()
}
for {}
}
此代码每次运行时都会打印各种数量的1。大部分时间它打印142 1&#39。
现在,有一个手动调用go scheduler的runtime.Gosched()。我正在玩它,发现只有在我调用两个 goroutines 和主要函数中的runtime.Gosched()时,我才能打印出256张1 p>
func main() {
procs := 256
runtime.GOMAXPROCS(procs)
for i := 0; i < procs; i++ {
go func() {
fmt.Print("1")
for { runtime.Gosched() }
}()
}
for { runtime.Gosched() }
}
有人可以解释为什么默认情况下不打印256个1并且我需要runtime.Gosched()?难道我们不能获得256个OS线程来运行这256个goroutine吗?为什么我们需要在两个地方调用runtime.Gosched()?
答案 0 :(得分:0)
我认为这是因为你可以在你的机器上创建一个线程限制。
runtime.GOMAXPROCS(256)
所做的是创建256个逻辑处理器
(也称为 P = Proccesor)并尝试运行它们。每个 P 都有自己的名为 G 的goroutines队列,并由OS线程执行(称为 M 为机器)。
所以会发生的是,你的256 P 试图获得一个OS线程( M )来执行 G ,但你的计算机没有'有足够多的资源来运行256个OS线程,这就是为什么你只得到143个。
对于我来说,执行此命令会产生143个,并且此过程的运行线程数约为150个。
因为默认情况下,调度程序不会抢占任何goroutine,你只会运行150个goroutine,而其他所有goroutine都会缺乏OS线程。这是描述此行为的issue。调用runtime.Gosched()
会产生处理器( P ),允许其他goroutine运行。
要检查调度程序正在执行的操作,可以添加GODEBUG环境变量:
env GODEBUG=scheddetail=1,schedtrace=1000 ./cpu3
这是输出(注意它使用 P,M,G术语):
SCHED 0ms: gomaxprocs=8 idleprocs=5 threads=5 spinningthreads=1 idlethreads=0 runqueue=0 gcwaiting=0 nmidlelocked=1 stopwait=0 sysmonwait=0
P0: status=1 schedtick=0 syscalltick=0 m=3 runqsize=0 gfreecnt=0
P1: status=1 schedtick=1 syscalltick=0 m=2 runqsize=0 gfreecnt=0
P2: status=1 schedtick=0 syscalltick=0 m=4 runqsize=0 gfreecnt=0
P3: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0
P4: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0
P5: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0
P6: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0
P7: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0
M4: p=2 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 helpgc=0 spinning=true blocked=false lockedg=-1
M3: p=0 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 helpgc=0 spinning=false blocked=false lockedg=-1
M2: p=1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 helpgc=0 spinning=true blocked=false lockedg=-1
M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 helpgc=0 spinning=false blocked=false lockedg=-1
M0: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 helpgc=0 spinning=false blocked=true lockedg=1
G1: status=1(chan receive) m=-1 lockedm=0
G2: status=4(force gc (idle)) m=-1 lockedm=-1
G3: status=4(GC sweep wait) m=-1 lockedm=-1
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111SCHED 1004ms: gomaxprocs=256 idleprocs=0 threads=150 spinningthreads=0 idlethreads=5 runqueue=0 gcwaiting=1 nmidlelocked=0 stopwait=143 sysmonwait=0
P0: status=1 schedtick=1 syscalltick=3 m=0 runqsize=0 gfreecnt=0
P1: status=1 schedtick=3 syscalltick=1 m=2 runqsize=0 gfreecnt=0
...
P141: status=1 schedtick=3 syscalltick=1 m=143 runqsize=0 gfreecnt=0
P142: status=1 schedtick=2 syscalltick=3 m=144 runqsize=0 gfreecnt=0
P143: status=3 schedtick=1 syscalltick=38 m=-1 runqsize=0 gfreecnt=0
...
P255: status=3 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0
M149: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 helpgc=0 spinning=false blocked=true lockedg=-1
...
M144: p=142 curg=181 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 helpgc=0 spinning=false blocked=false lockedg=-1
M143: p=141 curg=177 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 helpgc=0 spinning=false blocked=false lockedg=-1
M142: p=140 curg=179 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 helpgc=0 spinning=false blocked=false lockedg=-1
...
M112: p=110 curg=186 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 helpgc=0 spinning=false blocked=false lockedg=-1
...