為什么需要context
成都創(chuàng)新互聯(lián)是一家專業(yè)提供瀘水企業(yè)網(wǎng)站建設(shè),專注與網(wǎng)站建設(shè)、成都網(wǎng)站制作、HTML5建站、小程序制作等業(yè)務(wù)。10年已為瀘水眾多企業(yè)、政府機構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站建設(shè)公司優(yōu)惠進行中。
在go服務(wù)器中,對于每個請求的request都是在單獨的goroutine中進行的,處理一個request也可能設(shè)計多個goroutine之間的交互, 使用context可以使開發(fā)者方便的在這些goroutine里傳遞request相關(guān)的數(shù)據(jù)、取消goroutine的signal或截止日期
在并發(fā)程序中,由于超時、取消操作或者一些異常情況,往往需要進行搶占操作或者中斷后續(xù)操作。熟悉channel的朋友應(yīng)該都見過使用done channel來處理此類問題。比如以下這個例子:
上述例子中定義了一個buffer為0的channel done, 子協(xié)程運行著定時任務(wù)。如果主協(xié)程需要在某個時刻發(fā)送消息通知子協(xié)程中斷任務(wù)退出,那么就可以讓子協(xié)程監(jiān)聽這個done channel,一旦主協(xié)程關(guān)閉done channel,那么子協(xié)程就可以推出了,這樣就實現(xiàn)了主協(xié)程通知子協(xié)程的需求。這很好,但是這也是有限的。
如果我們可以在簡單的通知上附加傳遞額外的信息來控制取消:為什么取消,或者有一個它必須要完成的最終期限,更或者有多個取消選項,我們需要根據(jù)額外的信息來判斷選擇執(zhí)行哪個取消選項。
考慮下面這種情況:假如主協(xié)程中有多個任務(wù)1, 2, …m,主協(xié)程對這些任務(wù)有超時控制;而其中任務(wù)1又有多個子任務(wù)1, 2, …n,任務(wù)1對這些子任務(wù)也有自己的超時控制,那么這些子任務(wù)既要感知主協(xié)程的取消信號,也需要感知任務(wù)1的取消信號。
如果還是使用done channel的用法,我們需要定義兩個done channel,子任務(wù)們需要同時監(jiān)聽這兩個done channel。嗯,這樣其實好像也還行哈。但是如果層級更深,如果這些子任務(wù)還有子任務(wù),那么使用done channel的方式將會變得非常繁瑣且混亂。
我們需要一種優(yōu)雅的方案來實現(xiàn)這樣一種機制:
上層任務(wù)取消后,所有的下層任務(wù)都會被取消;中間某一層的任務(wù)取消后,只會將當(dāng)前任務(wù)的下層任務(wù)取消,而不會影響上層的任務(wù)以及同級任務(wù)。
這個時候context就派上用場了。我們首先看看context的結(jié)構(gòu)設(shè)計和實現(xiàn)原理。
context接口
先看Context接口結(jié)構(gòu),看起來非常簡單。
}
Context接口包含四個方法:
Deadline返回綁定當(dāng)前context的任務(wù)被取消的截止時間;如果沒有設(shè)定期限,將返回ok == false。
Done 當(dāng)綁定當(dāng)前context的任務(wù)被取消時,將返回一個關(guān)閉的channel;如果當(dāng)前context不會被取消,將返回nil。
Err 如果Done返回的channel沒有關(guān)閉,將返回nil;如果Done返回的channel已經(jīng)關(guān)閉,將返回非空的值表示任務(wù)結(jié)束的原因。如果是context被取消,Err將返回Canceled;如果是context超時,Err將返回DeadlineExceeded。
Value 返回context存儲的鍵值對中當(dāng)前key對應(yīng)的值,如果沒有對應(yīng)的key,則返回nil。
可以看到Done方法返回的channel正是用來傳遞結(jié)束信號以搶占并中斷當(dāng)前任務(wù);Deadline方法指示一段時間后當(dāng)前goroutine是否會被取消;以及一個Err方法,來解釋goroutine被取消的原因;而Value則用于獲取特定于當(dāng)前任務(wù)樹的額外信息。而context所包含的額外信息鍵值對是如何存儲的呢?其實可以想象一顆樹,樹的每個節(jié)點可能攜帶一組鍵值對,如果當(dāng)前節(jié)點上無法找到key所對應(yīng)的值,就會向上去父節(jié)點里找,直到根節(jié)點。
emptyCtx
emptyCtx是一個int類型的變量,但實現(xiàn)了context的接口。emptyCtx沒有超時時間,不能取消,也不能存儲任何額外信息,所以emptyCtx用來作為context樹的根節(jié)點。
Background和TODO只是用于不同場景下: Background通常被用于主函數(shù)、初始化以及測試中,作為一個頂層的context,也就是說一般我們創(chuàng)建的context都是基于Background;而TODO是在不確定使用什么context的時候才會使用。
用法 :
一般都是通過日志打印
譬如run方法里面加入以下
log.info("定時器啟動,時間:"+new Date())
try{
}catch(){
log.error("出現(xiàn)異常")
return;
}
log.info("定時器結(jié)束,時間:"+new Date())
當(dāng)日志打印有結(jié)束語句就證明跑完了
func?startTimer(f?func())?{
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
}
}()
}
利用 Etcd 的Lease租約特性來實現(xiàn)定時功能,同時通過Watch機制來實現(xiàn)多節(jié)點情況下只有一個節(jié)點執(zhí)行該任務(wù)。通過定時任務(wù)庫 Cron 的時間字符串解析器Parser來解析任務(wù)執(zhí)行時間。
Etcd
Cron
源碼鏈接
在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)的細節(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 包中, 也就是維護了一個任務(wù)池,任務(wù)池中的任務(wù)會有序的執(zhí)行,方便管理。
分享題目:go語言定時操作 go語言臨時指針
當(dāng)前路徑:http://m.rwnh.cn/article16/doocigg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站排名、網(wǎng)站制作、品牌網(wǎng)站設(shè)計、靜態(tài)網(wǎng)站、小程序開發(fā)、網(wǎng)站設(shè)計
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)