天天看點

golang學習之--context實作原理【轉載】

概述

    context

是Go中廣泛使用的程式包,由Google官方開發,在1.7版本引入。它用來簡化在多個go routine傳遞上下文資料、(手動/逾時)中止routine樹等操作,比如,官方http包使用context傳遞請求的上下文資料,gRpc使用context來終止某個請求産生的routine樹。每個Context應該視為隻讀的,通過WithCancel、WithDeadline、WithTimeout和WithValue函數可以基于現有的一個Context(稱為父Context)派生出一個新的Context(稱為子Context)。其中WithCancel、WithDeadline和WithTimeout函數除了傳回一個派生的Context以外,還會傳回一個與之關聯CancelFunc類型的函數,用于關閉Context。通過調用CancelFunc來關閉關聯的Context時,基于該Context所派生的Context也都會被關閉,并且會将自己從父Context中移除,停止和它相關的timer。

核心接口

1

2

3

4

5

6

type

Context 

interface

{

Deadline() (deadline time.Time, ok bool)

Done() <-

chan

struct

{}

Err() error

Value(key 

interface

{}) 

interface

{}

}

 根據如上接口顯示對每個方法介紹

  • Done

    會傳回一個channel,當該context被取消的時候,該channel會被關閉,同時對應的使用該context的routine也應該結束并傳回。
  • Context

    中的方法是協程安全的,這也就代表了在父routine中建立的context,可以傳遞給任意數量的routine并讓他們同時通路。
  • Deadline

    會傳回一個逾時時間,routine獲得了逾時時間後,可以對某些io操作設定逾時時間。
  • Value

    可以讓routine共享一些資料,當然獲得資料是協程安全的。

在請求處理的過程中,會調用各層的函數,每層的函數會建立自己的routine,是一個routine樹。是以,context也應該反映并實作成一棵樹。

建立context

  在建立context時會通過

context.Background

函數的傳回值是一個空的context,作為樹的根結點。并且提供4個常用函數去建立其子節點。

1

2

3

4

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

 需要注意的是每個函數都會傳回一個CancelFunc。調用CancelFunc對象将撤銷對應的Context對象,這樣父結點的所在的環境中,獲得了撤銷子節點context的權利,當觸發某些條件時,可以調用CancelFunc對象來終止子結點樹的所有routine。

我們在子集中可以通過 

cxt.Done(

)是否為空來判斷。

1

2

3

4

select

{

case

<-cxt.Done():

// do some cleaning and return

}

WithDeadline

WithTimeout

WithCancel

多了一個時間參數,它訓示context存活的最長時間。如果超過了過期時間,會自動撤銷它的子context。是以context的生命期是由父context的routine和

deadline

共同決定的。

WithValue

傳回parent的一個副本,該副本儲存了傳入的key/value,而調用Context接口的Value(key)方法就可以得到val。注意在同一個context中設定key/value,若key相同,值會被覆寫。

示例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

package

main

import

(

"fmt"

"time"

"golang.org/x/net/context"

)

func

main() {

ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*5)

ctx = context.WithValue(ctx, 

"Test"

"123456"

)

defer

cancelFunc()

if

t, ok := ctx.Deadline(); ok {

fmt.Println(time.Now())

fmt.Println(t.String())

}

go

func

(ctx context.Context) {

fmt.Println(ctx.Value(

"Test"

))

for

{

select

{

case

<-ctx.Done():

fmt.Println(ctx.Err())

return

default

:

continue

}

}

}(ctx)

time.Sleep(time.Second * 3)

繼續閱讀