golang?墻上時鐘與單調(diào)時鐘的實現(xiàn)
墻上時鐘與單調(diào)時鐘
墻上時鐘
墻上時鐘也稱為墻上時間。大多是1970年1月1日(UTC)以來的秒數(shù)和毫秒數(shù)。
墻上時間可以和NTP(Network Time Protocal,網(wǎng)絡(luò)時間協(xié)議)同步,但是如果本地時鐘遠(yuǎn)遠(yuǎn)快于NTP服務(wù)器,則強制重置之后會跳到先前某個時間點。(這里不是很確定,猜測是如果時間差的不多,則調(diào)整石英晶體振蕩器的頻率,慢慢一致。如果差很多,則強行一致)
單調(diào)時鐘
機器大多有自己的石英晶體振蕩器,并將其作為計時器。單調(diào)時鐘的絕對值沒有任何意義,根據(jù)操作系統(tǒng)和語言的不同,單調(diào)時鐘可能在程序開始時設(shè)為0、或在計算機啟動后設(shè)為0等等。但是通過比較同一臺計算機上兩次單調(diào)時鐘的差,可以獲得相對準(zhǔn)確的時間間隔。
Time的結(jié)構(gòu)
type Time struct {
// wall and ext encode the wall time seconds, wall time nanoseconds,
// and optional monotonic clock reading in nanoseconds.
//
// From high to low bit position, wall encodes a 1-bit flag (hasMonotonic),
// a 33-bit seconds field, and a 30-bit wall time nanoseconds field.
// The nanoseconds field is in the range [0, 999999999].
// If the hasMonotonic bit is 0, then the 33-bit field must be zero
// and the full signed 64-bit wall seconds since Jan 1 year 1 is stored in ext.
// If the hasMonotonic bit is 1, then the 33-bit field holds a 33-bit
// unsigned wall seconds since Jan 1 year 1885, and ext holds a
// signed 64-bit monotonic clock reading, nanoseconds since process start.
wall uint64
ext int64
...
}
復(fù)制代碼wall和ext共同記錄了時間,但是分為兩種情況,一種是沒有記錄單調(diào)時鐘(比如是通過字符串解析得到的時間),另一種是記錄了單調(diào)時鐘(比如通過Now)。
wall的第一位是一個標(biāo)記位
如果為1,則表示記錄了單調(diào)時鐘。則wall的2-34(閉區(qū)間)位記錄了從1885-1-1到現(xiàn)在的秒數(shù),最后30位記錄了納秒數(shù)。而ext記錄了從程序開始運行到現(xiàn)在經(jīng)過的單調(diào)時鐘數(shù)。
如果為0,則表示沒有記錄單調(diào)時鐘。則wall的2-34(閉區(qū)間)位全部為0(那最后30位是啥?)。而ext記錄了從1-1-1到現(xiàn)在經(jīng)過的秒數(shù)。
Since的實現(xiàn)

這里比較關(guān)鍵的代碼是第914行的runtimeNano() - startNano。startNano的含義還是直接上代碼比較明了。
var startNano = 0
?
func init(){
startNano = runtimeNano()
}runtimeNano()是調(diào)用了匯編,獲取了操作系統(tǒng)當(dāng)前的單調(diào)時鐘。前面說過,單調(diào)時鐘的絕對值沒有什么意義。因此這里將兩個時間相減,得到了從程序開始到現(xiàn)在的單調(diào)時鐘。
然后看一下Sub
func (t Time) Sub(u Time) Duration {
if t.wall&u.wall&hasMonotonic != 0 {
te := t.ext
ue := u.ext
d := Duration(te - ue)
if d < 0 && te > ue {
return maxDuration // t - u is positive out of range
}
if d > 0 && te < ue {
return minDuration // t - u is negative out of range
}
return d
}
d := Duration(t.sec()-u.sec())*Second + Duration(t.nsec()-u.nsec())
// Check for overflow or underflow.
switch {
case u.Add(d).Equal(t):
return d // d is correct
case t.Before(u):
return minDuration // t - u is negative out of range
default:
return maxDuration // t - u is positive out of range
}
}這里我們只需要關(guān)注2-13行即可。除去了范圍檢查,這里的主要邏輯就是兩個Time的ext相減。而ext又都代表了單調(diào)時鐘,所以最后返回的是單調(diào)時鐘的差值。
小結(jié)
在分布式系統(tǒng)中,我們經(jīng)常需要判斷時間間隔來檢測心跳。而墻上時鐘與NTP的組合可能會帶來時間的前后跳躍與閃爍,所以使用單調(diào)時鐘更加安全和保險。
在go語言中,沒有直接調(diào)用調(diào)用時鐘的函數(shù)??梢酝ㄟ^time.Now()獲得帶單調(diào)時鐘的Time結(jié)構(gòu)體,并通過Since和Until獲得相對準(zhǔn)確的時間間隔。
參考資料
go1.14.2 源碼
數(shù)據(jù)密集型應(yīng)用系統(tǒng)設(shè)計(書)
到此這篇關(guān)于golang 墻上時鐘與單調(diào)時鐘的實現(xiàn)的文章就介紹到這了,更多相關(guān)golang 墻上時鐘與單調(diào)時鐘內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang?pprof監(jiān)控之cpu占用率統(tǒng)計原理詳解
經(jīng)過前面的幾節(jié)對pprof的介紹,對pprof統(tǒng)計的原理算是掌握了七八十了,但唯獨還沒有分析pprof?工具是如何統(tǒng)計cpu使用情況的,今天我們來分析下這部分2023-04-04
Golang測試func?TestXX(t?*testing.T)的使用詳解
一般Golang中的測試代碼都以xxx_test.go的樣式,在命名測試函數(shù)的時候以Testxx開頭,下面給大家介紹Golang測試func?TestXX(t?*testing.T)的使用,感興趣的朋友跟隨小編一起看看吧2024-08-08
Go語言面向?qū)ο笾械亩鄳B(tài)你學(xué)會了嗎
面向?qū)ο笾械亩鄳B(tài)(Polymorphism)是指一個對象可以具有多種不同的形態(tài)或表現(xiàn)方式,本文將通過一些簡單的示例為大家講解一下多態(tài)的實現(xiàn),需要的可以參考下2023-07-07

