最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

GoLang中的timer定時(shí)器實(shí)現(xiàn)原理分析

 更新時(shí)間:2023年02月02日 11:16:50   作者:raoxiaoya  
Timer中對(duì)外暴露的只有一個(gè)channel,這個(gè) channel也是定時(shí)器的核心。當(dāng)計(jì)時(shí)結(jié)束時(shí),Timer會(huì)發(fā)送值到channel中,外部環(huán)境在這個(gè) channel 收到值的時(shí)候,就代表計(jì)時(shí)器超時(shí)了,可與select搭配執(zhí)行一些超時(shí)邏輯
// NewTimer creates a new Timer that will send
// the current time on its channel after at least duration d.
func NewTimer(d Duration) *Timer {
	c := make(chan Time, 1)
	t := &Timer{
		C: c,
		r: runtimeTimer{
			when: when(d),
			f:    sendTime,
			arg:  c,
		},
	}
	startTimer(&t.r)
	return t
}
// The Timer type represents a single event.
// When the Timer expires, the current time will be sent on C,
// unless the Timer was created by AfterFunc.
// A Timer must be created with NewTimer or AfterFunc.
type Timer struct {
	C <-chan Time
	r runtimeTimer
}
func NewTicker(d Duration) *Ticker {
	if d <= 0 {
		panic(errors.New("non-positive interval for NewTicker"))
	}
	// Give the channel a 1-element time buffer.
	// If the client falls behind while reading, we drop ticks
	// on the floor until the client catches up.
	c := make(chan Time, 1)
	t := &Ticker{
		C: c,
		r: runtimeTimer{
			when:   when(d),
			period: int64(d),
			f:      sendTime,
			arg:    c,
		},
	}
	startTimer(&t.r)
	return t
}
type Ticker struct {
	C <-chan Time // The channel on which the ticks are delivered.
	r runtimeTimer
}

ticker 跟 timer 的初始化過(guò)程差不多,但是 ticker 比 timer 多了一個(gè) period 參數(shù),意為間隔的意思。

// Interface to timers implemented in package runtime.
// Must be in sync with ../runtime/time.go:/^type timer
type runtimeTimer struct {
	pp       uintptr
	when     int64 //觸發(fā)時(shí)間
	period   int64 //執(zhí)行周期性任務(wù)的時(shí)間間隔
	f        func(any, uintptr) // 執(zhí)行的回調(diào)函數(shù),NOTE: must not be closure
	arg      any //執(zhí)行任務(wù)的參數(shù)
	seq      uintptr //回調(diào)函數(shù)的參數(shù),該參數(shù)僅在 netpoll 的應(yīng)用場(chǎng)景下使用
	nextwhen int64 //如果是周期性任務(wù),下次執(zhí)行任務(wù)時(shí)間
	status   uint32 //狀態(tài)
}
// sendTime does a non-blocking send of the current time on c.
func sendTime(c any, seq uintptr) {
	select {
	case c.(chan Time) <- Now():
	default:
	}
}

sendTime 采用非阻塞的形式,意為,不管是否存在接收方,此定時(shí)器一旦到時(shí)間了就要觸發(fā)掉。

// runtime/runtime2.go
type p struct {
    .....
    // The when field of the first entry on the timer heap.
	// This is updated using atomic functions.
	// This is 0 if the timer heap is empty.
    // 堆頂元素什么時(shí)候執(zhí)行
	timer0When uint64
    // The earliest known nextwhen field of a timer with
	// timerModifiedEarlier status. Because the timer may have been
	// modified again, there need not be any timer with this value.
	// This is updated using atomic functions.
	// This is 0 if there are no timerModifiedEarlier timers.
    // 如果有timer修改為更早執(zhí)行時(shí)間了,將會(huì)將執(zhí)行時(shí)間更新到更早時(shí)間
	timerModifiedEarliest uint64
    // Lock for timers. We normally access the timers while running
	// on this P, but the scheduler can also do it from a different P.
    // 操作timer的互斥鎖
	timersLock mutex
    // Actions to take at some time. This is used to implement the
	// standard library's time package.
	// Must hold timersLock to access.
    //該p 上的所有timer,必須加鎖去操作這個(gè)字段,因?yàn)椴煌膒 操作這個(gè)字段會(huì)有競(jìng)爭(zhēng)關(guān)系
	timers []*timer
	// Number of timers in P's heap.
	// Modified using atomic instructions.
    //p 堆上所有的timer數(shù)
	numTimers uint32
    // Number of timerDeleted timers in P's heap.
	// Modified using atomic instructions.
    //被標(biāo)記為刪除的timer,要么是我們調(diào)用stop,要么是timer 自己觸發(fā)后過(guò)期導(dǎo)致的刪除
	deletedTimers uint32
}
// runtime/time.go
type timer struct {
	// If this timer is on a heap, which P's heap it is on.
	// puintptr rather than *p to match uintptr in the versions
	// of this struct defined in other packages.
	pp puintptr
	// Timer wakes up at when, and then at when+period, ... (period > 0 only)
	// each time calling f(arg, now) in the timer goroutine, so f must be
	// a well-behaved function and not block.
	//
	// when must be positive on an active timer.
	when   int64
	period int64
	f      func(any, uintptr)
	arg    any
	seq    uintptr
	// What to set the when field to in timerModifiedXX status.
	nextwhen int64
	// The status field holds one of the values below.
	status uint32
}
// startTimer adds t to the timer heap.
//go:linkname startTimer time.startTimer
func startTimer(t *timer) {
	if raceenabled {
		racerelease(unsafe.Pointer(t))
	}
	addtimer(t)
}
// stopTimer stops a timer.
// It reports whether t was stopped before being run.
//go:linkname stopTimer time.stopTimer
func stopTimer(t *timer) bool {
	return deltimer(t)
}
// addtimer adds a timer to the current P.
// This should only be called with a newly created timer.
// That avoids the risk of changing the when field of a timer in some P's heap,
// which could cause the heap to become unsorted.
func addtimer(t *timer) {
	// when must be positive. A negative value will cause runtimer to
	// overflow during its delta calculation and never expire other runtime
	// timers. Zero will cause checkTimers to fail to notice the timer.
	if t.when <= 0 {
		throw("timer when must be positive")
	}
	if t.period < 0 {
		throw("timer period must be non-negative")
	}
	if t.status != timerNoStatus {
		throw("addtimer called with initialized timer")
	}
	t.status = timerWaiting
	when := t.when
	// Disable preemption while using pp to avoid changing another P's heap.
    // 如果M在此之后被別的P搶占了,那么后續(xù)操作的就是別的P上的timers,這是不允許的
	mp := acquirem()
	pp := getg().m.p.ptr()
	lock(&pp.timersLock)
	cleantimers(pp) // 清理掉已經(jīng)過(guò)期的timer,以提高添加和刪除timer的效率。
	doaddtimer(pp, t) // 執(zhí)行添加操作
	unlock(&pp.timersLock)
    // 調(diào)用 wakeNetPoller 方法,喚醒網(wǎng)絡(luò)輪詢器,檢查計(jì)時(shí)器被喚醒的時(shí)間(when)是
    // 否在當(dāng)前輪詢預(yù)期運(yùn)行的時(shí)間(pollerPollUntil)內(nèi),若是喚醒。
    // 有的定時(shí)器是伴隨著網(wǎng)絡(luò)輪訓(xùn)器的,比如設(shè)置的 i/o timeout
    // This can have a spurious wakeup but should never miss a wakeup
    // 寧愿出現(xiàn)錯(cuò)誤的喚醒,也不能漏掉一個(gè)喚醒
	wakeNetPoller(when)
	releasem(mp)
}
// 將0位置的timer與下面的子節(jié)點(diǎn)比較,如果比子節(jié)點(diǎn)大則下移。子節(jié)點(diǎn)i*4 + 1,i*4 + 2,i*4 + 3,i*4 + 4
siftdownTimer(pp.timers, 0) 
// 將i位置的timer與上面的父節(jié)點(diǎn)比較,如果比父節(jié)點(diǎn)小則上移。父節(jié)點(diǎn)是(i - 1) / 4
siftupTimer(pp.timers, i) 

timer 存儲(chǔ)在P中的 timers []*timer成員屬性上。timers看起來(lái)是一個(gè)切片,但是它是按照runtimeTimer.when這個(gè)數(shù)值排序的小頂堆四叉樹(shù),觸發(fā)時(shí)間越早越排在前面。

整體來(lái)講就是父節(jié)點(diǎn)一定比其子節(jié)點(diǎn)小,子節(jié)點(diǎn)之間沒(méi)有任何關(guān)系和大小的要求。

關(guān)于acquiremreleasem

//go:nosplit
func acquirem() *m {
	_g_ := getg()
	_g_.m.locks++
	return _g_.m
}
//go:nosplit
func releasem(mp *m) {
	_g_ := getg()
	mp.locks--
	if mp.locks == 0 && _g_.preempt {
		// restore the preemption request in case we've cleared it in newstack
		_g_.stackguard0 = stackPreempt
	}
}

acquirem函數(shù)獲取當(dāng)前M,并禁止M被搶占,因?yàn)镸被搶占時(shí)的判斷如下

//C:\Go\src\runtime\preempt.go +287
func canPreemptM(mp *m) bool {
   return mp.locks == 0 && mp.mallocing == 0 && mp.preemptoff == "" && mp.p.ptr().status == _Prunning
}
  • 運(yùn)行時(shí)沒(méi)有禁止搶占(m.locks == 0
  • 運(yùn)行時(shí)沒(méi)有在執(zhí)行內(nèi)存分配(m.mallocing == 0
  • 運(yùn)行時(shí)沒(méi)有關(guān)閉搶占機(jī)制(m.preemptoff == ""
  • M 與 P 綁定且沒(méi)有進(jìn)入系統(tǒng)調(diào)用(p.status == _Prunning

timers的觸發(fā)

// runtime/proc.go
func checkTimers(pp *p, now int64) (rnow, pollUntil int64, ran bool)
// runtime/time.go
func runtimer(pp *p, now int64) int64
func runOneTimer(pp *p, t *timer, now int64)

runtime/time.go文件中提供了checkTimers/runtimer/runOneTimer三個(gè)方法。checkTimers方法中,如果當(dāng)前p的timers長(zhǎng)度不為0,就不斷地調(diào)用runtimers。runtimes會(huì)根據(jù)堆頂?shù)膖imer的狀態(tài)判斷其能否執(zhí)行,如果可以執(zhí)行就調(diào)用runOneTimer實(shí)際執(zhí)行。

觸發(fā)定時(shí)器的途徑有兩個(gè)

  • 通過(guò)調(diào)度器在調(diào)度時(shí)進(jìn)行計(jì)時(shí)器的觸發(fā),findrunnable, schedule, stealWork。
  • 通過(guò)系統(tǒng)監(jiān)控檢查并觸發(fā)計(jì)時(shí)器(到期未執(zhí)行),sysmon。

調(diào)度器的觸發(fā)一共分兩種情況,一種是在調(diào)度循環(huán)的時(shí)候調(diào)用 checkTimers 方法進(jìn)行計(jì)時(shí)器的觸發(fā)。另外一種是當(dāng)前處理器 P 沒(méi)有可執(zhí)行的 Timer,且沒(méi)有可執(zhí)行的 G。那么按照調(diào)度模型,就會(huì)去竊取其他計(jì)時(shí)器和 G。

即使是通過(guò)每次調(diào)度器調(diào)度和竊取的時(shí)候觸發(fā),但畢竟是具有一定的隨機(jī)和不確定性,因此系統(tǒng)監(jiān)控觸發(fā)依然是一個(gè)兜底保障,在 Go 語(yǔ)言中 runtime.sysmon 方法承擔(dān)了這一個(gè)責(zé)任,存在觸發(fā)計(jì)時(shí)器的邏輯,在每次進(jìn)行系統(tǒng)監(jiān)控時(shí),都會(huì)在流程上調(diào)用 timeSleepUntil 方法去獲取下一個(gè)計(jì)時(shí)器應(yīng)觸發(fā)的時(shí)間,以及保存該計(jì)時(shí)器已打開(kāi)的計(jì)時(shí)器堆的 P。

在獲取完畢后會(huì)馬上檢查當(dāng)前是否存在 GC,若是正在 STW 則獲取調(diào)度互斥鎖。若發(fā)現(xiàn)下一個(gè)計(jì)時(shí)器的觸發(fā)時(shí)間已經(jīng)過(guò)去,則重新調(diào)用 timeSleepUntil 獲取下一個(gè)計(jì)時(shí)器的時(shí)間和相應(yīng) P 的地址。檢查 sched.sysmonlock 所花費(fèi)的時(shí)間是否超過(guò) 50μs。若是,則有可能前面所獲取的下一個(gè)計(jì)時(shí)器觸發(fā)時(shí)間已過(guò)期,因此重新調(diào)用 timeSleepUntil 方法再次獲取。如果發(fā)現(xiàn)超過(guò) 10ms 的時(shí)間沒(méi)有進(jìn)行 netpoll 網(wǎng)絡(luò)輪詢,則主動(dòng)調(diào)用 netpoll 方法觸發(fā)輪詢。同時(shí)如果存在不可搶占的處理器 P,則調(diào)用 startm 方法來(lái)運(yùn)行那些應(yīng)該運(yùn)行,但沒(méi)有在運(yùn)行的計(jì)時(shí)器。

到此這篇關(guān)于GoLang中的timer定時(shí)器實(shí)現(xiàn)原理分析的文章就介紹到這了,更多相關(guān)Go timer定時(shí)器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語(yǔ)言常見(jiàn)錯(cuò)誤之a(chǎn)ny沒(méi)傳遞任何信息解決分析

    Go語(yǔ)言常見(jiàn)錯(cuò)誤之a(chǎn)ny沒(méi)傳遞任何信息解決分析

    Go語(yǔ)言,由于其高效強(qiáng)大的并行處理能力和優(yōu)雅簡(jiǎn)單的設(shè)計(jì)哲學(xué),一直以來(lái)都是編程世界的寵兒,然而,對(duì)于一些Go新手和甚至熟悉Go的程序員也可能會(huì)遇到一個(gè)常見(jiàn)的錯(cuò)誤:?any沒(méi)傳遞任何信息,那么,如何規(guī)避這個(gè)錯(cuò)誤,本文將揭示其中的秘密
    2024-01-01
  • Go語(yǔ)言為什么不支持前綴自增運(yùn)算符原理解析

    Go語(yǔ)言為什么不支持前綴自增運(yùn)算符原理解析

    這篇文章主要為大家介紹了Go語(yǔ)言為什么不支持前綴自增運(yùn)算符原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • 詳解golang函數(shù)多返回值錯(cuò)誤處理與error類型

    詳解golang函數(shù)多返回值錯(cuò)誤處理與error類型

    這篇文章主要為大家詳細(xì)介紹了golang中函數(shù)多返回值錯(cuò)誤處理與error類型的相關(guān)知識(shí),文中的示例代碼簡(jiǎn)潔易懂,感興趣的小伙伴快跟隨小編一起學(xué)習(xí)吧
    2023-10-10
  • 一文帶你了解Golang中類型轉(zhuǎn)換庫(kù)cast的使用

    一文帶你了解Golang中類型轉(zhuǎn)換庫(kù)cast的使用

    你是否在使用 Go 的過(guò)程中因?yàn)轭愋娃D(zhuǎn)換的繁瑣而苦惱過(guò)?你是否覺(jué)得 Go 語(yǔ)言中的類型斷言可能會(huì) panic 而對(duì)自己寫(xiě)的代碼有那么一點(diǎn)點(diǎn)不放心?本文就為大家推薦一個(gè)用于類型轉(zhuǎn)換的第三方庫(kù) cast 絕對(duì)是一個(gè)值得嘗試的選擇
    2023-02-02
  • golang拼接字符串的5種實(shí)現(xiàn)方式

    golang拼接字符串的5種實(shí)現(xiàn)方式

    文章總結(jié)了五種拼接字符串的方式,包括使用 "+"、strings.Join()、fmt.Sprint、bytes.Buffer 和 strings.Builder,每種方式都有其適用場(chǎng)景和性能特點(diǎn)
    2024-12-12
  • golang gin 框架 異步同步 goroutine 并發(fā)操作

    golang gin 框架 異步同步 goroutine 并發(fā)操作

    這篇文章主要介紹了golang gin 框架 異步同步 goroutine 并發(fā)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • golang?基于?mysql?簡(jiǎn)單實(shí)現(xiàn)分布式讀寫(xiě)鎖

    golang?基于?mysql?簡(jiǎn)單實(shí)現(xiàn)分布式讀寫(xiě)鎖

    這篇文章主要介紹了golang?基于mysql簡(jiǎn)單實(shí)現(xiàn)分布式讀寫(xiě)鎖,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09
  • golang 設(shè)置web請(qǐng)求狀態(tài)碼操作

    golang 設(shè)置web請(qǐng)求狀態(tài)碼操作

    這篇文章主要介紹了golang 設(shè)置web請(qǐng)求狀態(tài)碼操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • go語(yǔ)言中if語(yǔ)句用法實(shí)例

    go語(yǔ)言中if語(yǔ)句用法實(shí)例

    這篇文章主要介紹了go語(yǔ)言中if語(yǔ)句用法,以實(shí)例形式分析了if語(yǔ)句的定義及使用技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-02-02
  • 深入Golang的接口interface

    深入Golang的接口interface

    這篇文章主要介紹了深入Golang的接口interface,go不要求類型顯示地聲明實(shí)現(xiàn)了哪個(gè)接口,只要實(shí)現(xiàn)了相關(guān)的方法即可,編譯器就能檢測(cè)到,接下來(lái)關(guān)于接口interface的相關(guān)介紹需要的朋友可以參考下面文章內(nèi)容
    2022-06-06

最新評(píng)論

固始县| 仙游县| 阜宁县| 建平县| 都匀市| 尼木县| 山阳县| 互助| 汽车| 沙湾县| 西乌珠穆沁旗| 梅河口市| 大安市| 舟山市| 克山县| 故城县| 厦门市| 容城县| 镇坪县| 独山县| 鹤壁市| 方正县| 宕昌县| 北宁市| 卓资县| 曲水县| 德清县| 夏河县| 海晏县| 婺源县| 喀什市| 邵阳县| 南平市| 疏附县| 赣州市| 金华市| 改则县| 凉城县| 惠水县| 星座| 桐城市|