Go中的context包在Go1.7的時候由google貢獻引入Go SDK中。在併發的場景下基本都會用到context這個包的功能。 

什麼是context?

中文翻譯爲“上下文”,具體應用在goroutine中。用作goroutine的控制手段。context 主要用來在各個goroutine之間傳遞上下文。包括:cancel,timeout等。(個人在開發中用作超時控制,類似:前端一個require但是後端比較繁忙。比如5s無法返給前端response。這樣就直接返回timeout並且取消這個goroutine。)

隨着context 包的引入,標準庫的很多接口帶上ctx 參數。類似 database/sql 。context基本就是成爲控制併發控制&&超時控制的標準用法。可以看某站的源碼也是攜帶大量的context。

爲什麼會需要context

如果你用過Go寫過http。基本上幾行代碼就可以起來一個server。

在這個server裏面,通常一個require就會啓動至少一個goroutine來工作;注意是至少一個goroutine。

這些goroutine需要共享這個require的基本數據。例如token,處理require的最大時間(超時才返回數據,請求方超時請求不到)等等。還有就是這麼個場景,請求方只是點錯了,來了界面,然後立刻關閉了。這樣就要快速釋放掉創建的goroutine。因爲他們已經沒用了,他們的結果已經不被需要。當所有的goroutine退出,操作系統就能回收掉這些資源。

還有就是 在Go中server是個“攜程模型”,也就是至少一個攜程對應處理一個請求。我本人的真實經歷(測試環境還好。沒造成影響。):再測試的時候,併發訪問。某個處理模塊慢了,當前系統的請求沒有控制,然後超時時間也沒有設置。那樣等着的goroutine的越來越多。雖然Go官方號稱一個goroutine大概是2KB的消耗。但是吧。。看proof的時候 gorouitne達到10W個。內存飆漲。服務不可用。操作系統load飆升。整個服務對外不可用。如果是生產基本就是P0事故。基本就要提桶跑路。

後面我改掉代碼。通過設置timeout就避免這個問題。我給請求其他模塊的時間設置到Xs.如果超過這個時間沒有receive。就直接給請求方一個err或者服務器繁忙。

概括一下:在Go中,我們不能直接killgoroutine,攜程的關閉基本就是靠return,Channel。但是在某些情景下面,像我上面說的一個require生成很多子攜程,互相之間有關聯。需要共享global data。在用channel就挺難操作。

context 用來解決goroutine之間的 退出通知 元數據傳遞

context底層實現原理

Go 版本 1.13.1

整體概覽:

context包的代碼量並不是很多。算上註釋也就不到4 500行。真實用的代碼估計不到200行。這玩意還是挺有意思的。

網上抄了個context的包的具體內容。

context的接口

在pre_go19.go這個文件中定義的context接口

type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}Err() error
Value(key interface{}) interface{}}複製代碼

Context時是個interface。定義4個func。都是冪等。也就是說多次調用一個method。得到結果是一樣的。

第一方法Done().返回一個channel,可以表示context被去取消的信號:當這個channel被關閉/取消,說明context被取消。注意,這就是個RO的channel。Go基礎,讀取一個關閉的channel只會讀到這個管道對應的類型的0值。也就是可以認爲 是個<-channel  類型的channel。因此子攜程裏讀這個channel。除非被close掉。否則沒有任何東西能取到。也就是可以利用這個特點:從字攜程取到0值。就可以做收尾,快速退出。回收資源。

其他的下次在寫吧。挺晚了。20200614 23.40。:pray:

歡迎關注我們的微信公衆號,每天學習Go知識

相關文章