func?startTimer(f?func())?{
目前創(chuàng)新互聯(lián)已為超過千家的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)站空間、網(wǎng)站托管運(yùn)營、企業(yè)網(wǎng)站設(shè)計、合作網(wǎng)站維護(hù)等服務(wù),公司將堅持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。
go?func()?{
for?{
f()
now?:=?time.Now()
//?計算下一個零點
next?:=?now.Add(time.Hour?*?24)
next?=?time.Date(next.Year(),?next.Month(),?next.Day(),?0,?0,?0,?0,?next.Location())
t?:=?time.NewTimer(next.Sub(now))
-t.C
}
}()
}
在linux下實現(xiàn)定時器主要有如下方式
在這當(dāng)中 基于時間輪方式實現(xiàn)的定時器 時間復(fù)雜度最小,效率最高,然而我們可以通過 優(yōu)先隊列 實現(xiàn)時間輪定時器。
優(yōu)先隊列的實現(xiàn)可以使用最大堆和最小堆,因此在隊列中所有的數(shù)據(jù)都可以定義排序規(guī)則自動排序。我們直接通過隊列中 pop 函數(shù)獲取數(shù)據(jù),就是我們按照自定義排序規(guī)則想要的數(shù)據(jù)。
在 Golang 中實現(xiàn)一個優(yōu)先隊列異常簡單,在 container/head 包中已經(jīng)幫我們封裝了,實現(xiàn)的細(xì)節(jié),我們只需要實現(xiàn)特定的接口就可以。
下面是官方提供的例子
因為優(yōu)先隊列底層數(shù)據(jù)結(jié)構(gòu)是由二叉樹構(gòu)建的,所以我們可以通過數(shù)組來保存二叉樹上的每一個節(jié)點。
改數(shù)組需要實現(xiàn) Go 預(yù)先定義的接口 Len , Less , Swap , Push , Pop 和 update 。
timerType結(jié)構(gòu)是定時任務(wù)抽象結(jié)構(gòu)
首先的 start 函數(shù),當(dāng)創(chuàng)建一個 TimeingWheel 時,通過一個 goroutine 來執(zhí)行 start ,在start中for循環(huán)和select來監(jiān)控不同的channel的狀態(tài)
通過for循環(huán)從隊列中取數(shù)據(jù),直到該隊列為空或者是遇見第一個當(dāng)前時間比任務(wù)開始時間大的任務(wù), append 到 expired 中。因為優(yōu)先隊列中是根據(jù) expiration 來排序的,
所以當(dāng)取到第一個定時任務(wù)未到的任務(wù)時,表示該定時任務(wù)以后的任務(wù)都未到時間。
當(dāng) getExpired 函數(shù)取出隊列中要執(zhí)行的任務(wù)時,當(dāng)有的定時任務(wù)需要不斷執(zhí)行,所以就需要判斷是否該定時任務(wù)需要重新放回優(yōu)先隊列中。 isRepeat 是通過判斷任務(wù)中 interval 是否大于 0 判斷,
如果大于0 則,表示永久就生效。
防止外部濫用,阻塞定時器協(xié)程,框架又一次封裝了timer這個包,名為 timer_wapper 這個包,它提供了兩種調(diào)用方式。
參數(shù)和上面的參數(shù)一樣,只是在第三個參數(shù)中使用了任務(wù)池,將定時任務(wù)放入了任務(wù)池中。定時任務(wù)的本身執(zhí)行就是一個 put 操作。
至于put以后,那就是 workers 這個包管理的了。在 worker 包中, 也就是維護(hù)了一個任務(wù)池,任務(wù)池中的任務(wù)會有序的執(zhí)行,方便管理。
利用 Etcd 的Lease租約特性來實現(xiàn)定時功能,同時通過Watch機(jī)制來實現(xiàn)多節(jié)點情況下只有一個節(jié)點執(zhí)行該任務(wù)。通過定時任務(wù)庫 Cron 的時間字符串解析器Parser來解析任務(wù)執(zhí)行時間。
Etcd
Cron
源碼鏈接
可以學(xué)習(xí)黑馬程序員的這個教程
20小時快速入門go語言:網(wǎng)頁鏈接
go語言的優(yōu)勢
可直接編譯成機(jī)器碼,不依賴其他庫,glibc的版本有一定要求,部署就是扔一個文件上去就完成了。
靜態(tài)類型語言,但是有動態(tài)語言的感覺,靜態(tài)類型的語言就是可以在編譯的時候檢查出來隱藏的大多數(shù)問題,動態(tài)語言的感覺就是有很多的包可以使用,寫起來的效率很高。
語言層面支持并發(fā),這個就是Go最大的特色,天生的支持并發(fā)。Go就是基因里面支持的并發(fā),可以充分的利用多核,很容易的使用并發(fā)。
內(nèi)置runtime,支持垃圾回收,這屬于動態(tài)語言的特性之一吧,雖然目前來說GC(內(nèi)存垃圾回收機(jī)制)不算完美,但是足以應(yīng)付我們所能遇到的大多數(shù)情況,特別是Go1.1之后的GC。
簡單易學(xué),Go語言的作者都有C的基因,那么Go自然而然就有了C的基因,那么Go關(guān)鍵字是25個,但是表達(dá)能力很強(qiáng)大,幾乎支持大多數(shù)你在其他語言見過的特性:繼承、重載、對象等。
豐富的標(biāo)準(zhǔn)庫,Go目前已經(jīng)內(nèi)置了大量的庫,特別是網(wǎng)絡(luò)庫非常強(qiáng)大。
內(nèi)置強(qiáng)大的工具,Go語言里面內(nèi)置了很多工具鏈,最好的應(yīng)該是gofmt工具,自動化格式化代碼,能夠讓團(tuán)隊review變得如此的簡單,代碼格式一模一樣,想不一樣都很困難。
跨平臺編譯,如果你寫的Go代碼不包含cgo,那么就可以做到window系統(tǒng)編譯linux的應(yīng)用,如何做到的呢?Go引用了plan9的代碼,這就是不依賴系統(tǒng)的信息。
內(nèi)嵌C支持,Go里面也可以直接包含C代碼,利用現(xiàn)有的豐富的C庫。
一般都是通過日志打印
譬如run方法里面加入以下
log.info("定時器啟動,時間:"+new Date())
try{
}catch(){
log.error("出現(xiàn)異常")
return;
}
log.info("定時器結(jié)束,時間:"+new Date())
當(dāng)日志打印有結(jié)束語句就證明跑完了