在我們編碼過(guò)程中,經(jīng)常會(huì)用到與時(shí)間相關(guān)的需求。而關(guān)于時(shí)間轉(zhuǎn)換之類的比較簡(jiǎn)單,那么計(jì)時(shí)器經(jīng)過(guò)了以下幾個(gè)版本的迭代:
10多年專業(yè)網(wǎng)站制作公司歷程,堅(jiān)持以創(chuàng)新為先導(dǎo)的網(wǎng)站服務(wù),服務(wù)超過(guò)成百上千家企業(yè)及個(gè)人,涉及網(wǎng)站設(shè)計(jì)、重慶App定制開(kāi)發(fā)、微信開(kāi)發(fā)、平面設(shè)計(jì)、互聯(lián)網(wǎng)整合營(yíng)銷等多個(gè)領(lǐng)域。在不同行業(yè)和領(lǐng)域給人們的工作和生活帶來(lái)美好變化。
Go1.10之前的計(jì)時(shí)器的結(jié)構(gòu)體如下所示
注意這個(gè)結(jié)構(gòu)體是用var變量定義的,它會(huì)存儲(chǔ)所有的計(jì)時(shí)器。t就是最小四叉堆,運(yùn)行時(shí)創(chuàng)建的所有計(jì)時(shí)器都會(huì)加入到四叉堆中。
由一個(gè)獨(dú)立的timerproc通過(guò)最小四叉堆和futexsleep來(lái)管理定時(shí)任務(wù)。
但是全局四叉堆共用一把鎖對(duì)性能的影響非常大,所以Go1.10之后將全局四叉堆分割成了64個(gè)更小的四叉堆。
在理想情況下,四叉堆的數(shù)量應(yīng)該等于處理器的數(shù)量GOMAXPROCS,但是需要?jiǎng)討B(tài)獲取處理的數(shù)量,所以經(jīng)過(guò)權(quán)衡初始化64個(gè)四叉堆,如果當(dāng)前機(jī)器的處理器P的個(gè)數(shù)超過(guò)了64個(gè),多個(gè)處理器的計(jì)時(shí)器可能會(huì)存儲(chǔ)在同一個(gè)桶中。
將全局計(jì)時(shí)器分片,雖然能夠降低鎖的粒度,但是timerproc造成處理器和線程之間頻繁的上下文切換卻成為了影響計(jì)時(shí)器的瓶頸。
在Go1.14版本之后,計(jì)時(shí)器桶timersBucket已經(jīng)被移除了,所有的計(jì)時(shí)器都以最小四叉堆的形式存儲(chǔ)在P中。
在p結(jié)構(gòu)體中有以下字段與計(jì)時(shí)器關(guān)聯(lián):
而計(jì)時(shí)器的結(jié)構(gòu)體為:
這個(gè)僅僅只是runtime/time.go運(yùn)行時(shí)內(nèi)部處理的結(jié)構(gòu),而真正對(duì)外暴露的計(jì)時(shí)器的結(jié)構(gòu)體是:
通過(guò)channel來(lái)通知計(jì)時(shí)器時(shí)間
在runtime/time.go文件下,我們可以看到下面幾個(gè)方法:
當(dāng)通過(guò)time.NewTimer方法增加新的計(jì)時(shí)器時(shí),會(huì)執(zhí)行startTimer來(lái)增加計(jì)時(shí)器
狀態(tài)從timerNoStatus-timerWaiting,其他狀態(tài)會(huì)拋出異常
1、調(diào)用cleantimers清除處理器P中的計(jì)時(shí)器,可以加快創(chuàng)建和刪除計(jì)時(shí)器的程序速度
2、調(diào)用doaddtimer將當(dāng)前計(jì)時(shí)器加入到處理器P的四叉堆timers中
3、調(diào)用wakeNetPoller喚醒網(wǎng)絡(luò)輪詢器中休眠的線程,檢查timer被喚醒的時(shí)間when是否在當(dāng)前輪詢預(yù)期的運(yùn)行時(shí)間內(nèi),如果是就喚醒。
當(dāng)通過(guò)調(diào)用timer.Stop停止計(jì)時(shí)器時(shí),會(huì)執(zhí)行stopTimer來(lái)停止計(jì)時(shí)器
deltimer會(huì)標(biāo)記需要?jiǎng)h除的計(jì)時(shí)器。在刪除計(jì)時(shí)器的過(guò)程中,可能會(huì)遇到其他處理器P的計(jì)時(shí)器,所以我們僅僅只是將狀態(tài)標(biāo)記為刪除,處理器P執(zhí)行刪除操作。
當(dāng)通過(guò)調(diào)用timer.Reset重置定時(shí)器時(shí),會(huì)執(zhí)行resetTimer來(lái)重置定時(shí)器
modtimer會(huì)修改已經(jīng)存在的計(jì)時(shí)器,會(huì)根據(jù)以下規(guī)則處理計(jì)時(shí)器狀態(tài)
狀態(tài)為timerNoStatus, timerRemoved會(huì)被標(biāo)記為已刪除wasRemoved,就會(huì)調(diào)用doaddtimer新創(chuàng)建一個(gè)計(jì)時(shí)器。
而在正常情況下會(huì)根據(jù)修改后的時(shí)間進(jìn)行不同的處理:
會(huì)根據(jù)狀態(tài)清除處理器P的最小四叉堆隊(duì)頭的計(jì)時(shí)器
在GPM調(diào)度的時(shí)候檢查計(jì)時(shí)器
與cleantimers不同的是,adjusttimers會(huì)遍歷處理器P轉(zhuǎn)給你所有的計(jì)時(shí)器
會(huì)檢查四叉堆堆頂?shù)挠?jì)時(shí)器,根據(jù)狀態(tài)處理計(jì)時(shí)器
1、狀態(tài)是timerDeleted,狀態(tài)變?yōu)閠imerDeleted,然后刪除計(jì)時(shí)器,再變更狀態(tài)為timerRemoved
2、狀態(tài)是timerModifiedXXX
3、狀態(tài)是timerWaiting,如果計(jì)時(shí)器沒(méi)有到達(dá)觸發(fā)時(shí)間,直接返回,否則狀態(tài)變?yōu)閠imerRunning,調(diào)用runOneTimer運(yùn)行堆頂?shù)挠?jì)時(shí)器
根據(jù)period字段是否大于0判斷,如果大于0
如果小于等于0:
更新完?duì)顟B(tài)后,回調(diào)函數(shù)f(arg, seq)執(zhí)行方法。
在adjesttimers中提到過(guò)
checkTimers是調(diào)度器用來(lái)運(yùn)行處理器P中定時(shí)器的函數(shù),會(huì)在以下幾種情況被觸發(fā):
1、先通過(guò)處理器P字段中updateTimer0When判斷是否有需要執(zhí)行的計(jì)時(shí)器,如果沒(méi)有直接返回
2、如果下一個(gè)計(jì)時(shí)器沒(méi)有到期但是需要?jiǎng)h除的計(jì)時(shí)器較少時(shí)會(huì)直接返回
3、加鎖
4、需要處理的timer,根據(jù)時(shí)間將timers切片中的timer重新排序,調(diào)用adjusttimers
5、會(huì)通過(guò)runtimer依次查找運(yùn)行計(jì)時(shí)器
6、處理器中已刪除的timer大于p上的timer數(shù)量的1/4,對(duì)標(biāo)記為timerDeleted的timer進(jìn)行清理
7、解鎖
go1.10最多可以創(chuàng)建GOMAXPROCS數(shù)量的timerproc協(xié)程,當(dāng)然不超過(guò)64。但我們要知道timerproc自身就是協(xié)程,也需要runtime pmg的調(diào)度。到go 1.14把檢查到期定時(shí)任務(wù)的工作交給了網(wǎng)絡(luò)輪詢器,不需要額外的調(diào)度,每次runtime.schedule和findrunable時(shí)直接運(yùn)行到期的定時(shí)任務(wù)。
rune是Go語(yǔ)言中一種特殊的數(shù)據(jù)類型,它是int32的別名,幾乎在所有方面等同于int32,用于區(qū)分字符值和整數(shù)值,官方解釋如下:
下面我們通過(guò)一個(gè)例子來(lái)看一下:
我們猜測(cè)一下結(jié)果,hello5 個(gè)字符+1 個(gè)空格+3 個(gè)漢子,算起來(lái)應(yīng)該是 9 個(gè),長(zhǎng)度為 9 才對(duì),但是我們執(zhí)行一下,
結(jié)果打印是 15,這是為什么呢?
所以計(jì)算出的長(zhǎng)度就等于 5+1+3*3=15
如果我們需要計(jì)算出字符串的長(zhǎng)度,而不是底層字節(jié)的個(gè)數(shù),那么可以使用下面的方法:
運(yùn)行結(jié)果如下:
在 rune 定義上方還有一個(gè),byte = uint8
這樣。不過(guò)只是個(gè)精確到納秒的計(jì)時(shí)器,不是精確到納秒的當(dāng)前時(shí)間。windows好像只能拿到ms精度的當(dāng)前時(shí)間吧,不是很清楚。
package main
import (
"syscall"
"time"
"unsafe"
)
func NewStopWatch() func() time.Duration {
var QPCTimer func() func() time.Duration
QPCTimer = func() func() time.Duration {
lib, _ := syscall.LoadLibrary("kernel32.dll")
qpc, _ := syscall.GetProcAddress(lib, "QueryPerformanceCounter")
qpf, _ := syscall.GetProcAddress(lib, "QueryPerformanceFrequency")
if qpc == 0 || qpf == 0 {
return nil
}
var freq, start uint64
syscall.Syscall(qpf, 1, uintptr(unsafe.Pointer(freq)), 0, 0)
syscall.Syscall(qpc, 1, uintptr(unsafe.Pointer(start)), 0, 0)
if freq = 0 {
return nil
}
freqns := float64(freq) / 1e9
return func() time.Duration {
var now uint64
syscall.Syscall(qpc, 1, uintptr(unsafe.Pointer(now)), 0, 0)
return time.Duration(float64(now-start) / freqns)
}
}
var StopWatch func() time.Duration
if StopWatch = QPCTimer(); StopWatch == nil {
// Fallback implementation
start := time.Now()
StopWatch = func() time.Duration { return time.Since(start) }
}
return StopWatch
}
func main() {
// Call a new stop watch to create one from this moment on.
watch := NewStopWatch()
// Do some stuff that takes time.
time.Sleep(1)
// Call the stop watch itself and it will return a time.Duration
dur := watch()
}
這和語(yǔ)言沒(méi)關(guān)系,操作系統(tǒng)要提供這樣的原語(yǔ)。linux和windows都是可以的。
標(biāo)題名稱:go語(yǔ)言計(jì)時(shí)器 go語(yǔ)言定時(shí)任務(wù)
標(biāo)題路徑:http://vcdvsql.cn/article24/doicjce.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供自適應(yīng)網(wǎng)站、面包屑導(dǎo)航、移動(dòng)網(wǎng)站建設(shè)、全網(wǎng)營(yíng)銷推廣、品牌網(wǎng)站建設(shè)、微信小程序
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)