Golang標準庫——context

context

context包定義了上下文類型,該類型在API邊界之間以及進程之間傳遞截止日期,取消信號和其他請求范圍的值。

對服務器的傳入請求應創建一個Context,而對服務器的傳出調用應接受一個Context。它們之間的函數調用鏈必須傳播Context,可以選擇將其替換為使用WithCancel,WithDeadline,WithTimeout或WithValue創建的派生Context。取消context后,從該Context派生的所有Context也會被取消。

WithCancel,WithDeadline和WithTimeout函數采用Context(父級)并返回派生的Context(子級)和CancelFunc。調用CancelFunc會取消該子代及其子代,刪除父代對該子代的引用,并停止所有關聯的計時器。未能調用CancelFunc會使子代及其子代泄漏,直到父代被取消或計時器觸發。審核工具檢查所有控制流路徑上是否都使用了CancelFuncs。

使用Context的程序應遵循以下規則,以使各個包之間的接口保持一致,并啟用靜態分析工具來檢查上下文傳播:

  • 不要將Context存儲在結構類型中;
  • 將Context明確傳遞給需要它的每個函數;
  • Context應該是第一個參數,通常命名為ctx:
func DoSomething(ctx context.Context, arg Arg) error {
  // ... use ctx ...
}

即使函數允許,也不要傳遞nil Context。如果不確定使用哪個上下文,請傳遞context.TODO。

僅將Context值用于傳遞流程和API的請求范圍的數據,而不用于將可選參數傳遞給函數。

可以將相同的Context傳遞給在不同goroutine中運行的函數。Context對于由多個goroutine同時使用是安全的。

Variables

var Canceled = errors.New("context canceled")
var DeadlineExceeded error = deadlineExceededError{}

Canceled是Context.Err類型,取消Context時返回的錯誤。

DeadlineExceeded是上下文的最后期限過去時Context.Err返回的錯誤。

type CancelFunc

type CancelFunc func()

CancelFunc告訴操作停止工作。 CancelFunc不等待工作停止。 在第一個調用之后,隨后對CancelFunc的調用什么都不做。

type Context

type Context interface {

    Deadline() (deadline time.Time, ok bool) 
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
    
}

其中

  • Deadline方法需要返回當前Context被取消的時間,也就是完成工作的截止時間(deadline);
  • Done方法需要返回一個Channel,這個Channel會在當前工作完成或者上下文被取消之后關閉,多次調用Done方法會返回同一個Channel
  • Err方法會返回當前Context結束的原因,它只會在Done返回的Channel被關閉時才會返回非空的值;
    • 如果當前Context被取消就會返回Canceled錯誤;
    • 如果當前Context超時就會返回DeadlineExceeded錯誤;
  • Value方法會從Context中返回鍵對應的值,對于同一個上下文來說,多次調用Value并傳入相同的Key會返回相同的結果,該方法僅用于傳遞跨API和進程間跟請求域的數據;

func Background

func Background() Context

Background返回一個非空的Context。 它永遠不會被取消,沒有值,也沒有期限。 它通常由主要功能,初始化和測試使用,并用作傳入請求的頂級Context。

func TODO

func TODO() Context

TODO返回一個非空的Context。 當不清楚要使用哪個上下文或尚不可用時(因為尚未擴展周圍的功能以接受Context參數),代碼應使用context.TODO。 靜態分析工具可識別TODO,該工具可確定上下文是否在程序中正確傳播。

func WithCancel

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

WithCancel返回具有新的“完成”通道的父級副本。 當調用返回的cancel函數或關閉父上下文的Done通道時(以先發生的為準),關閉返回的上下文的Done通道。

取消此上下文將釋放與其關聯的資源,因此在此上下文中運行的操作完成后,代碼應立即調用cancel。

func main() {
   gen := func(ctx context.Context) <-chan int {
      dst := make(chan int)
      n := 1
      go func() {
         for {
            select {
            case <-ctx.Done():
               return // returning not to leak the goroutine
            case dst <- n:
               n++
            }
         }
      }()
      return dst
   }

   ctx, cancel := context.WithCancel(context.Background())
   defer cancel() // cancel when we are finished consuming integers

   for n := range gen(ctx) {
      fmt.Println(n)
      if n == 5 {
         break
      }
   }
}

上面的示例代碼中,gen函數在單獨的goroutine中生成整數并將它們發送到返回的通道。 gen的調用者在使用生成的整數之后需要取消上下文,以免gen啟動的內部goroutine發生泄漏。

func WithDeadline

func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)

WithDeadline返回父上下文的副本,并將截止日期調整為不遲于d。 如果父母的截止日期早于d,則WithDeadline(parent,d)在語義上等同于父母。 當截止日期到期,調用返回的cancel函數或關閉父上下文的Done通道(以先到者為準)時,將關閉返回的上下文的Done通道。

取消此上下文將釋放與其關聯的資源,因此在此上下文中運行的操作完成后,代碼應立即調用cancel。

func main() {
   d := time.Now().Add(50 * time.Millisecond)
   ctx, cancel := context.WithDeadline(context.Background(), d)

   // Even though ctx will be expired, it is good practice to call its
   // cancelation function in any case. Failure to do so may keep the
   // context and its parent alive longer than necessary.
   defer cancel()

   select {
   case <-time.After(1 * time.Second):
      fmt.Println("overslept")
   case <-ctx.Done():
      fmt.Println(ctx.Err())
   }
}

上面的代碼中,定義了一個50毫秒之后過期的deadline,然后我們調用context.WithDeadline(context.Background(), d)得到一個上下文(ctx)和一個取消函數(cancel),然后使用一個select讓主程序陷入等待:等待1秒后打印overslept退出或者等待ctx過期后退出。 因為ctx50毫秒后就過期,所以ctx.Done()會先接收到值,上面的代碼會打印ctx.Err()取消原因。

func WithTimeout

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

WithTimeout返回WithDeadline(parent, time.Now().Add(timeout))方法。

取消此Context 將釋放與之關聯的資源,因此在此Context 中運行的操作完成后,代碼應立即調用cancel:

func main() {
   ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
   defer cancel()

   select {
   case <-time.After(1 * time.Second):
      fmt.Println("overslept")
   case <-ctx.Done():
      fmt.Println(ctx.Err()) // prints "context deadline exceeded"
   }
}

func WithValue

func WithValue(parent Context, key, val interface{}) Context

WithValue返回父項的副本,其中與key關聯的值為val。

僅將Context值用于傳遞流程和API的請求范圍的數據,而不用于將可選參數傳遞給函數。

提供的密鑰必須具有可比性,并且不能為字符串類型或任何其他內置類型,以避免使用上下文在程序包之間發生沖突。 WithValue的用戶應定義自己的密鑰類型。 為了避免在分配給接口{}時進行分配,上下文鍵通常具有具體的類型struct {}。 另外,導出的上下文鍵變量的靜態類型應該是指針或接口。

func main() {
   type favContextKey string

   f := func(ctx context.Context, k favContextKey) {
      if v := ctx.Value(k); v != nil {
         fmt.Println("found value:", v)
         return
      }
      fmt.Println("key not found:", k)
   }

   k := favContextKey("language")
   ctx := context.WithValue(context.Background(), k, "Go")

   f(ctx, k)
   f(ctx, favContextKey("color"))
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容