time.Sleep(1) 后發(fā)生了什么
Time.sleep(d duration)方法會(huì)阻塞一個(gè)協(xié)程的執(zhí)行直到d時(shí)間結(jié)束。
實(shí)現(xiàn)原理很簡(jiǎn)單,但內(nèi)部代碼實(shí)現(xiàn)卻是大有文章,每個(gè)go版本的timer的實(shí)現(xiàn)都有所不同,本文基于go1.14,接下來(lái)分別從宏觀和圍觀介紹一遍主要調(diào)度過(guò)程。
圖文演示



階段一、進(jìn)入睡眠
?
//go:linkname timeSleep time.Sleepfunc timeSleep(ns int64) {if ns <= 0 { //判斷入?yún)⑹欠裾?/span>return}gp := getg() //獲取當(dāng)前的goroutine???t?:=?gp.timer //如果不存在timer,new一個(gè)if t == nil {t = new(timer)gp.timer = t}t.f = goroutineReady //后面喚醒時(shí)候會(huì)用到,修改goroutine狀態(tài)為goreadyt.arg = gpt.nextwhen = nanotime() + ns //記錄上喚醒時(shí)間gopark(resetForSleep, unsafe.Pointer(t), waitReasonSleep, traceEvGoSleep, 1) //調(diào)用gopark掛起goroutine}
func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason waitReason, traceEv byte, traceskip int) {???......省略了大部分代碼?????mp.waitlock = lock //由于runningG和p沒(méi)有連接,將timer賦值到當(dāng)前m上,后面會(huì)給到pmp.waitunlockf = unlockf //將函數(shù)付給m......???mcall(park_m) //將當(dāng)前的g停放}
?func?park_m(gp?*g)?{_g_ := getg() //獲取當(dāng)前goroutine......casgstatus(gp, _Grunning, _Gwaiting) //將goroutine狀態(tài)設(shè)為waitingdropg()if fn := _g_.m.waitunlockf; fn != nil { //獲取到mresetForSleep函數(shù)ok := fn(gp, _g_.m.waitlock) //返回值是true_g_.m.waitunlockf = nil //清空該m的函數(shù)空間_g_.m.waitlock = nil //......... }schedule() //觸發(fā)新的調(diào)速循環(huán),可執(zhí)行隊(duì)列中獲取g到m上進(jìn)行調(diào)度}
func modtimer(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) {......loop:for {switch status = atomic.Load(&t.status); status {......case timerNoStatus, timerRemoved: //由于剛創(chuàng)建,所以timer為默認(rèn)值0,對(duì)應(yīng)timerNoStatusmp = acquirem()if atomic.Cas(&t.status, status, timerModifying) {wasRemoved = true //設(shè)置標(biāo)志位為truebreak loop}releasem(mp)badTimer()}}t.period = period???t.f?=?f?//上文傳過(guò)來(lái)的goroutineReady函數(shù),用于將g轉(zhuǎn)變?yōu)閞unnable狀態(tài)t.arg = arg //上文的g實(shí)例t.seq = seq???if?wasRemoved?{?//會(huì)執(zhí)行到此處t.when = when??????pp?:=?getg().m.p.ptr()?//獲取當(dāng)前的p的指針??????lock(&pp.timersLock)?//加鎖,為了并發(fā)安全,因?yàn)閠imer可以去其他的p偷取??????doaddtimer(pp,?t)?//添加定時(shí)器到當(dāng)前的p??????unlock(&pp.timersLock)?//解鎖??????if?!atomic.Cas(&t.status,?timerModifying,?timerWaiting)?{ //轉(zhuǎn)變到timerWaitingbadTimer()}??????......}
當(dāng)觸發(fā)完gopark方法,會(huì)調(diào)用releasem(mp)方法釋放當(dāng)前goroutine與m的連接后,該goroutine脫離當(dāng)前的m掛起,進(jìn)入gwaiting狀態(tài),不在任何運(yùn)行隊(duì)列上。對(duì)應(yīng)上圖2。
階段二、恢復(fù)執(zhí)行
func checkTimers(pp *p, now int64) (rnow, pollUntil int64, ran bool) {......省略掉調(diào)整計(jì)時(shí)器時(shí)間的一些步驟???lock(&pp.timersLock)?//加鎖adjusttimers(pp) //調(diào)整計(jì)時(shí)器的時(shí)間rnow = nowif len(pp.timers) > 0 {if rnow == 0 {rnow = nanotime()}for len(pp.timers) > 0 {?????????if?tw?:=?runtimer(pp,?rnow);?tw?!=?0?{?//進(jìn)入runtimer方法,攜帶系統(tǒng)時(shí)間參數(shù)與處理器if tw > 0 {pollUntil = tw}break}ran = true}}......}
func runtimer(pp *p, now int64) int64 {for {??????t?:=?pp.timers[0]?//遍歷堆頂?shù)亩〞r(shí)器.......switch s := atomic.Load(&t.status); s {case timerWaiting: //經(jīng)過(guò)time.Sleep的定時(shí)器會(huì)是waiting狀態(tài)if t.when > now { //判斷是否超過(guò)時(shí)間// Not ready to run.return t.when}if !atomic.Cas(&t.status, s, timerRunning) { //修改計(jì)時(shí)器狀態(tài)continue?????????}?????????runOneTimer(pp,?t,?now)?//運(yùn)行該計(jì)時(shí)器函數(shù)return 0 ........
func runOneTimer(pp *p, t *timer, now int64) {........???f?:=?t.f?//goready函數(shù)???arg?:=?t.arg //就是之前傳入的goroutine???seq?:=?t.seq? //默認(rèn)值0if t.period > 0 {......... //由于period為默認(rèn)值0,會(huì)走else里面} else {dodeltimer0(pp) //刪除該計(jì)時(shí)器在p中,該timer在0坐標(biāo)位if !atomic.Cas(&t.status, timerRunning, timerNoStatus) { //設(shè)置為nostatusbadTimer()}}.......unlock(&pp.timersLock)f(arg, seq) //執(zhí)行g(shù)oroutineReady方法,喚起等待的goroutine.........}
func goroutineReady(arg interface{}, seq uintptr) {???goready(arg.(*g),?0)?//該處傳入的第二個(gè)參數(shù)代表調(diào)度到運(yùn)行隊(duì)列的位置,該處設(shè)置為0,說(shuō)明直接調(diào)度到運(yùn)行隊(duì)列即將要執(zhí)行的位置,等待被執(zhí)行。}
參考文章
【1】《Go計(jì)時(shí)器》https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-timer/
【2】《Golang定時(shí)器底層實(shí)現(xiàn)剖析》https://www.cyhone.com/articles/analysis-of-golang-timer/
推薦閱讀
評(píng)論
圖片
表情
