首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

go语言-Scheduler原理以及查看Goroutine执行

最近看了看go scheduler的基本原理,本文介绍go语言scheduler的基本原理以及如何查看go代码中的go routine的执行情况。

0)Scheduler(调度器)

熟悉go语言的小伙伴应该都使用过goroutine。goroutine就是Go语言提供的一种用户态线程。Scheduler是调度goroutine的调度器。

Go的调度器内部有三个重要概念:M,P,G。

M (machine): 代表真正的内核操作系统里面的线程,和POSIX里的thread差不多,也是真正执行goroutine逻辑的部分。

G (Goroutine): 代表一个goroutine。

P (Processor): 代表调度的上下文,可以理解成一个局部调度器。

Go语言实现了多个Goroutine到多个Processor的映射(调度)。注意的是,针对X个Processor,Scheduler可能创建多于X个M(有些M可能会暂时被block)。还需要理解额外两个概念:GRQ(Global Running Queue)以及 LRQ(Local Running Queue)。未指定Processor的Goroutine会存放在GRQ上,在调度到合适的Processor后,会将一个Goroutine从GRQ移动到LRQ。

Go程序中发生了四类事件,允许调程序做出调度决策。

a. 使用关键字go b. 垃圾收集 c. 系统调用 d. 同步

1)Processor的个数

Processor的个数可以通过GOMAXPROCS环境变量设置。GOMAXPROCS默认值是CPU的核数。Processor的个数可以通过如下的go代码进行查询:

package main

import(

"fmt"

"runtime"

)

func main(){

// NumCPU returns the number of logical

// CPUs usable by the current process.

fmt.Println(runtime.NumCPU())

}

也就是通过runtime.NumCPU函数可以获得Processor的个数。查看go语言的源代码(runtime/os_linux.c),NumCPU函数的实现函数如下:

funcgetproccount()int32 {

const maxCPUs = 64 * 1024

var buf[maxCPUs / 8]byte

r :=sched_getaffinity(,unsafe.Sizeof(buf),&buf[])

if r

return 1

}

n := int32()

for _,v := range buf[:r]{

for v != 0 {

n += int32(v & 1)

}

}

if n == 0 {

n = 1

}

return n

}

在linux操作系统中,通过调用sched_getaffinity函数获取cpu_set_t的信息,从而推算出CPU的个数。

2)查看Processor以及Goroutine的调度情况

在go程序命令行加上GODEBUG=schedtrace=1000,scheddetail=1选项,可以实时打印Goroutine的调度情况:schedtrace参数指定打印的时间间隔,scheddetail参数指定是否打印更多细节。

以下面的代码为例:

package main

import "fmt"

import "time"

func f(from string,index int){

fmt.Println("index: ",index)

var i int=0

for true {

i = i + 1

//time.Sleep(1 * time.Second)

}

}

func main(){

var i int= 0

for true {

go f("goroutine",i)

i+=1

time.Sleep(1 * time.Second)

}

}

这段go程序,不停的创建Goroutine,每个Goroutine执行f函数。注意,f函数中也是一个for循环(默认的情况下Sleep函数不起作用)。

如果只用schedtrace=1000参数,在MAC笔记本上的输出结果如下:

index: 0

index: 1

SCHED 0ms: gomaxprocs=4 idleprocs=2 threads=5 spinningthreads=0 idlethreads=1 runqueue=0[0 0 0 0]

index: 2

SCHED 1009ms: gomaxprocs=4 idleprocs=1 threads=6 spinningthreads=0 idlethreads=1 runqueue=0[0 0 0 0]

index: 3

SCHED 2019ms: gomaxprocs=4 idleprocs=0 threads=6 spinningthreads=0 idlethreads=0 runqueue=0[0 0 0 0]

SCHED 3020ms: gomaxprocs=4 idleprocs=0 threads=6 spinningthreads=0 idlethreads=1 runqueue=1[0 0 0 0]

SCHED 4021ms: gomaxprocs=4 idleprocs=0 threads=6 spinningthreads=0 idlethreads=1 runqueue=1[0 0 0 0]

SCHED 5030ms: gomaxprocs=4 idleprocs=0 threads=6 spinningthreads=0 idlethreads=1 runqueue=1[0 0 0 0]

一行SCHED开头的log打印出了当前时刻Scheduler的状态:

gomaxprocs:多少个Processor

idleProcs:多少个空闲的Processor

threads:多少个M

idlethreads:多少个空闲thread

runqueue:GRQ以及LRQ的状态,第一个数字是GRQ中的Goroutine的数量,[ ] 中的数字是各个LRQ中的Goroutine的数量。

你会发现上述的go代码中只跑了4个Goroutine,不会创建新的Goroutine,而且GRQ中一直有个Goroutine永远调度不到。原因是f函数是个死循环,不会释放Processor。

如果在f函数中加入延时,则会不停的创建以及调度Goroutine。

如果使用scheddetail=1,会打印出具体的Thread的执行信息。

总结:goroutine就是Go语言提供的一种用户态线程。Scheduler是调度goroutine的调度器。Go的调度器内部由三部分组成:M(Machine),P(Processor),G(Goroutine)。在go程序命令行加上GODEBUG=schedtrace=1000,scheddetail=1选项,可以实时打印Goroutine的调度情况。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181015G2791C00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券