標(biāo)準(zhǔn)庫 context 理解

Context 通常被譯作上下文乾胶,一般理解為程序單元的一個運行狀態(tài)封豪、現(xiàn)場谴轮、快照,而翻譯中上下文又很好地詮釋了它的本質(zhì)吹埠,上下則是指存在上下層的傳遞第步,上會把內(nèi)容傳遞給下。

在 Golang 中缘琅,程序單元也就是指的 goroutine粘都。每個 goroutine 在執(zhí)行之前,都要先知道程序當(dāng)前的執(zhí)行狀態(tài)刷袍,通常將這些執(zhí)行狀態(tài)封裝在一個 Context 變量中翩隧,傳遞給要執(zhí)行的 Goroutine 中。上下文則幾乎已經(jīng)成為傳遞與請求同生存周期變量的標(biāo)準(zhǔn)方法呻纹。

context 包不僅實現(xiàn)了在程序單元之間共享狀態(tài)變量的方法堆生,同時能通過簡單的方法,使我們在被調(diào)用程序單元的外部雷酪,通過設(shè)置 ctx 變量值淑仆,將過期或撤銷這些信號傳遞給被調(diào)用的單元。在網(wǎng)絡(luò)編程中太闺,若存在A調(diào)用B的API糯景,B再調(diào)用C的API,若A調(diào)用B取消省骂,則也要取消B調(diào)用C蟀淮,通過在A、B钞澳、C的API調(diào)用之間傳遞Context怠惶,以及判斷其狀態(tài),就能解決此問題轧粟,這是為什么 gRPC 的接口中帶上 ctx context.Context 參數(shù)的原因之一策治。

context 包的核心就是 Context 接口脓魏,其定義如下:

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}
  • Deadline 會返回一個超時時間,Goroutine 獲得了超時時間后通惫,例如可以對某些 io 操作設(shè)定超時時間茂翔。
  • Done 方法返回一個信道(channel),當(dāng) Context 被撤銷或過期時履腋,該信道是關(guān)閉的珊燎,即它是一個表示 Context 是否關(guān)閉的信號。
  • 當(dāng) Done 信道關(guān)閉后遵湖,Err方法表明 Context 被撤銷的原因悔政。
  • Value 可以讓 Goroutine 共享一些數(shù)據(jù),當(dāng)然獲得數(shù)據(jù)是協(xié)程安全的延旧。但是使用這些數(shù)據(jù)的時候谋国,需要注意同步,比如返回了一個 map迁沫,而這個 map 的讀寫則要加鎖芦瘾。

Context 接口沒有提供方法來設(shè)置其值和過期時間,也沒有提供方法直接將其自身撤銷弯洗。也就是說旅急,Context 不能改變和撤銷自身逢勾。那么該怎么通過 Context 傳遞改變后的狀態(tài)呢牡整?

context 使用

無論是 Goroutine,他們的創(chuàng)建和調(diào)用關(guān)系總是像層層調(diào)用進(jìn)行的溺拱,就像人的輩分一樣逃贝,而更靠頂部的 Goroutine 應(yīng)有辦法主動關(guān)閉其下屬的 Goroutine 的執(zhí)行(不然程序就可能失控了)。為了實現(xiàn)這種關(guān)系迫摔,Context 結(jié)構(gòu)也應(yīng)該是樹狀沐扳,葉子節(jié)點總由根節(jié)點衍生出來。

要創(chuàng)建 Context 樹句占,第一步就是要得到根節(jié)點沪摄, context.Background 函數(shù)的返回值就是根節(jié)點:

func Background() Context

該函數(shù)返回空的 Context,該 Context 一般由接收請求的第一個 Goroutine 創(chuàng)建纱烘,是與進(jìn)入請求對應(yīng)的 Context 根節(jié)點杨拐,它不能取消、沒有值擂啥、也沒有過期時間哄陶。它常常作為處理 Request 的頂層 context 存在。

有了根節(jié)點哺壶,又該怎么創(chuàng)建其他的子節(jié)點屋吨、孫節(jié)點蜒谤? context 包為我們提供了多個函數(shù)來創(chuàng)建他們:

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key interface{}, val interface{}) Context

函數(shù)接收一個 Context 類型的參數(shù) parent,并返回一個 Context 類型的值至扰,這樣就層層創(chuàng)建出不同的節(jié)點鳍徽。子節(jié)點是從復(fù)制父節(jié)點得到的,并且根據(jù)接收參數(shù)設(shè)定子節(jié)點的一些狀態(tài)值敢课,接著就可以將子節(jié)點傳遞給下層的 Goroutine 了旬盯。

再回到之前的問題:該怎么通過 Context 傳遞改變后的狀態(tài)呢?使用 Context 的 Goroutine 無法取消某個操作翎猛,其實是合理的胖翰,因為這些 Goroutine 是被某個父 Goroutine 創(chuàng)建的,而理應(yīng)只有父 Goroutine 可以取消操作切厘。在父 Goroutine 中可以通過 WithCancel 方法獲得一個 cancel 方法萨咳,從而獲得 cancel 的權(quán)利。

第一個 WithCancel 函數(shù)疫稿,它是將父節(jié)點復(fù)制到子節(jié)點培他,并且還返回一個額外的 CancelFunc 函數(shù)類型變量,該函數(shù)類型的定義為:

type CancelFunc func()

調(diào)用 CancelFunc 對象將撤銷對應(yīng)的 Context 對象遗座,這就是主動撤銷 Context 的方法舀凛。在父節(jié)點的 Context 所對應(yīng)的環(huán)境中,通過 WithCancel 函數(shù)不僅可創(chuàng)建子節(jié)點的 Context途蒋,同時也獲得了該節(jié)點 Context 的控制權(quán)猛遍,一旦執(zhí)行該函數(shù),則該節(jié)點 Context 就結(jié)束了号坡,則子節(jié)點需要類似如下代碼來判斷是否已結(jié)束懊烤,并退出該 Goroutine:

select {
case <-ctx.Done():
    // do some clean ...
}

WithDeadline 函數(shù)的作用也差不多,它返回 Context 的類型同樣是 parent 的副本宽堆,但其過期時間由 deadline 和 parent 的過期時間同時決定腌紧。當(dāng) parent 的過期時間早于傳入的 deadline 時間時,返回的過期時間應(yīng)與 parent 相同畜隶。父節(jié)點過期時壁肋,其所有的子孫節(jié)點必須同時關(guān)閉;反之籽慢,返回的父節(jié)點的過期時間則為 deadline浸遗。

WithTimeout 函數(shù)與 WithDeadline 類似,只不過它傳入的是從現(xiàn)在開始 Context 剩余的生命時長嗡综。他們都同樣返回了所創(chuàng)建的子 Context 的控制權(quán)乙帮,一個 CancelFunc 類型的函數(shù)變量。

當(dāng)頂層的 Request 請求函數(shù)結(jié)束后极景,我們就可以 cancel 掉某個 context察净,從而層層 Goroutine 根據(jù)判斷 ctx.Done() 來結(jié)束驾茴。

WithValue 函數(shù),它返回 parent 的一個函數(shù)副本氢卡,調(diào)用該副本的 Value(key) 方法將得到 val锈至。這樣,我們不光將根節(jié)點的原有的值保留了译秦,還在孫節(jié)點中加入了新的值峡捡,注意若存在Key相同,則會被覆蓋筑悴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末们拙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子阁吝,更是在濱河造成了極大的恐慌砚婆,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件突勇,死亡現(xiàn)場離奇詭異装盯,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)甲馋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進(jìn)店門埂奈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人定躏,你說我怎么就攤上這事账磺。” “怎么了共屈?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵绑谣,是天一觀的道長。 經(jīng)常有香客問我拗引,道長,這世上最難降的妖魔是什么幌衣? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任矾削,我火速辦了婚禮,結(jié)果婚禮上豁护,老公的妹妹穿的比我還像新娘哼凯。我一直安慰自己,他們只是感情好楚里,可當(dāng)我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布断部。 她就那樣靜靜地躺著,像睡著了一般班缎。 火紅的嫁衣襯著肌膚如雪蝴光。 梳的紋絲不亂的頭發(fā)上她渴,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天,我揣著相機(jī)與錄音蔑祟,去河邊找鬼趁耗。 笑死,一個胖子當(dāng)著我的面吹牛疆虚,可吹牛的內(nèi)容都是我干的苛败。 我是一名探鬼主播,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼径簿,長吁一口氣:“原來是場噩夢啊……” “哼罢屈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起篇亭,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤儡遮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后暗赶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鄙币,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年蹂随,在試婚紗的時候發(fā)現(xiàn)自己被綠了十嘿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡岳锁,死狀恐怖绩衷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情激率,我是刑警寧澤咳燕,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站乒躺,受9級特大地震影響招盲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嘉冒,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一曹货、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧讳推,春花似錦顶籽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春镊绪,著一層夾襖步出監(jiān)牢的瞬間匀伏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工镰吆, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留帘撰,地道東北人。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓万皿,卻偏偏與公主長得像摧找,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子牢硅,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內(nèi)容