Golang: runtime.Gosched 问题(3)

专门看看 runtime.Gosched()

专栏的介绍可以参考 《GotchaGolang专栏》,代码可以看《宝库-Gotcha》

这个函数的作用是让当前goroutine让出CPU,好让其它的goroutine获得执行的机会。同时,当前的goroutine也会在未来的某个时间点继续运行。

1
2
3
4
func Gosched
func Gosched()
Gosched yields the processor, allowing other goroutines to run.
It does not suspend the current goroutine, so execution resumes automatically.

案例

原来需要在 main 里面 sleep 或者 select{} 才能让其他 goroutines 有执行的机会

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
)

func showNumber (i int) {
fmt.Println(i)
}

func main() {

for i := 0; i < 10; i++ {
go showNumber(i)
}

fmt.Println("Haha")
}

执行结果:

1
Haha

没有打印出数字,可以看到goroutine没有获得机会运行。

修改代码:在main函数中加上runtime.Gosched():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"fmt"
"runtime"
)

func showNumber (i int) {
fmt.Println(i)
}

func main() {

for i := 0; i < 10; i++ {
go showNumber(i)
}

runtime.Gosched()
fmt.Println("Haha")
}

运行结果改变了:

1
2
3
4
5
6
7
8
9
10
11
0
1
2
3
4
5
6
7
8
9
Haha

具体和 goroutines 的实现有关,简单说 logical concurrent coroutines which do not map 1:1 to OS threads

具体可以参考这篇《文章》

摘要如下: (没有利用多核的状态下,一般使用的是一个线程,执行权都是这一个线程中交换&调度)
When you run Go program without specifying GOMAXPROCS environment variable, Go goroutines are scheduled for execution in single OS thread. However, to make program appear to be multithreaded (that’s what goroutines are for, aren’t they?), the Go scheduler must sometimes switch the execution context, so each goroutine could do its piece of work.

when GOMAXPROCS variable is not specified, Go runtime is only allowed to use one thread, so it is impossible to switch execution contexts while goroutine is performing some conventional work, like computations or even IO (which is mapped to plain C functions). The context can be switched only when Go concurrency primitives are used, e.g. when you switch on several chans, or (this is your case) when you explicitly tell the scheduler to switch the contexts - this is what runtime.Gosched is for.

So, in short, when execution context in one goroutine reaches Gosched call, the scheduler is instructed to switch the execution to another goroutine. In your case there are two goroutines, main (which represents ‘main’ thread of the program) and additional, the one you have created with go say. If you remove Gosched call, the execution context will never be transferred from the first goroutine to the second, hence no ‘world’ for you. When Gosched is present, the scheduler transfers the execution on each loop iteration from first goroutine to the second and vice versa, so you have ‘hello’ and ‘world’ interleaved.

Looks like that in newer versions of Go compiler Go runtime forces goroutines to yield not only on concurrency primitives usage, but on OS system calls too. This means that execution context can be switched between goroutines also on IO functions calls.

总结

goroutines 的 gosched 有点儿像以前线程模型中的 yeild,让出执行权。但是协程并不是线程。


Merlin 2018.3 协程调度

文章目录
  1. 1. 案例
  2. 2. 总结
|