golang學(xué)習(xí)筆記之-context

Context-用來(lái)管理調(diào)用上下文,控制一個(gè)請(qǐng)求的生命周期。

直接看代碼:Context是一個(gè)接口

type Context interface { 
    //返回代表該Context過(guò)期的時(shí)間,和表示deadline是否被設(shè)置的bool值葱淳。
    Deadline() (deadline time.Time, ok bool)
    //返回一個(gè)channel漾唉,關(guān)閉該channel就代表關(guān)閉該Context怒竿。返回nil代表該Context不需要被關(guān)閉
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}

Context這個(gè)接口共有4個(gè)方法:

  • Deadline方法是獲取設(shè)置的截止時(shí)間柜砾,第一個(gè)參數(shù)返回式是截時(shí)間轩拨,到了這個(gè)時(shí)間點(diǎn)践瓷,Context會(huì)自動(dòng)發(fā)起取消請(qǐng)求;第二個(gè)返回值ok==false時(shí)表示沒(méi)有設(shè)置截止時(shí)間亡蓉,如果需要取消的話晕翠,需要調(diào)用取消函數(shù)進(jìn)行取消。
  • Done方法返回一個(gè)只讀的chan砍濒,類型為struct{}淋肾,我們?cè)趃oroutine中,如果該方法返回的chan可以讀取爸邢,則意味著parent context已經(jīng)發(fā)起了取消請(qǐng)求樊卓,我們通過(guò)Done方法收到這個(gè)信號(hào)后,就應(yīng)該做清理操作杠河,然后退出goroutine碌尔,釋放資源赶掖。
  • Err方法返回取消的錯(cuò)誤原因,因?yàn)槭裁碈ontext被取消七扰。
  • Value方法獲取該Context上綁定的值,是一個(gè)鍵值對(duì)陪白,所以要通過(guò)一個(gè)Key才可以獲取對(duì)應(yīng)的值颈走,這個(gè)值一般是線程安全的。

以上四個(gè)方法中常用的就是Done咱士。如果Context取消的時(shí)候立由,我們就可以得到一個(gè)關(guān)閉的chan,關(guān)閉的chan是可以讀取的序厉,所以只要可以讀取的時(shí)候锐膜,就意味著收到Context取消的信號(hào)了,以下是這個(gè)方法的經(jīng)典用法弛房。

func Stream(ctx context.Context, out chan<- Value) error {
    for {
        v, err := DoSomething(ctx)
        if err != nil {
            return err
        }
        select {
        case <-ctx.Done():
            return ctx.Err()
        case out <- v:
        }
    }
  }

Context接口并不需要我們實(shí)現(xiàn)道盏,Go內(nèi)置已經(jīng)幫我們實(shí)現(xiàn)了2個(gè),我們代碼中最開(kāi)始都是以這兩個(gè)內(nèi)置的作為最頂層的partent context文捶,衍生出更多的子Context荷逞。這些 Context 對(duì)象形成一棵樹(shù):當(dāng)一個(gè) Context 對(duì)象被取消時(shí),繼承自它的所有 Context 都會(huì)被取消粹排。兩個(gè)實(shí)現(xiàn)如下:

var (
    background = new(emptyCtx)
    todo       = new(emptyCtx)
)

func Background() Context {
    return background
}

func TODO() Context {
    return todo
}
  • Background:主要用于main函數(shù)种远、初始化以及測(cè)試代碼中,作為Context這個(gè)樹(shù)結(jié)構(gòu)的最頂層的Context顽耳,也就是根Context坠敷。
  • TODO:它目前還不知道具體的使用場(chǎng)景,如果我們不知道該使用什么Context的時(shí)候射富,可以使用這個(gè)膝迎。

上面Background和TODO方法:都是emptyCtx結(jié)構(gòu)體類型,是一個(gè)不可取消胰耗。沒(méi)有設(shè)置截止時(shí)間弄抬,沒(méi)有攜帶任何值的Context。

// emptyCtx 不需要關(guān)閉宪郊,沒(méi)有任何鍵值對(duì)掂恕,也沒(méi)有過(guò)期時(shí)間。
type emptyCtx int

func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
    return
}

func (*emptyCtx) Done() <-chan struct{} {
    return nil
}

func (*emptyCtx) Err() error {
    return nil
}

func (*emptyCtx) Value(key interface{}) interface{} {
    return nil
}

emptyCtx實(shí)現(xiàn)Context接口的方法:可以看到這些方法什么都沒(méi)做弛槐,返回的都是nil或者零值懊亡。

Context的繼承衍生

有了如上的根Context,那么是如何衍生更多的子Context的呢乎串?這就要靠context包為我們提供的With系列的函數(shù)了店枣。

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, val interface{}) Context
  • 這四個(gè)With函數(shù):接收的都有一個(gè)partent參數(shù),就是父Context。我們要基于這個(gè)父Context創(chuàng)建出子Context的意思鸯两,這種方式可以理解為子Context對(duì)父Context的繼承闷旧,也可以理解為基于父Context的衍生。
  • 通過(guò)這些函數(shù)钧唐,就創(chuàng)建了一顆Context樹(shù)忙灼,樹(shù)的每個(gè)節(jié)點(diǎn)都可以有任意多個(gè)子節(jié)點(diǎn),節(jié)點(diǎn)層級(jí)可以有任意多個(gè)钝侠。
  • WithCancel函數(shù):傳遞一個(gè)父Context作為參數(shù)该园,返回子Context以及一個(gè)取消函數(shù)用來(lái)取消Context。
  • WithDeadline函數(shù):和WithCancel差不多帅韧,它會(huì)多傳遞一個(gè)截止時(shí)間參數(shù)里初,意味著到了這個(gè)時(shí)間點(diǎn),會(huì)自動(dòng)取消Context忽舟,當(dāng)然我們也可以不等到這個(gè)時(shí)候双妨,可以提前通過(guò)取消函數(shù)進(jìn)行取消。
  • WithTimeout和WithDeadline基本上一樣叮阅,這個(gè)表示是超時(shí)自動(dòng)取消斥难,是多少時(shí)間后自動(dòng)取消Context的意思。
  • WithValue函數(shù)和取消Context無(wú)關(guān)帘饶,它是為了生成一個(gè)綁定了一個(gè)鍵值對(duì)數(shù)據(jù)的Context哑诊,這個(gè)綁定的數(shù)據(jù)可以通過(guò)Context.Value方法訪問(wèn)到,后面我們會(huì)專門講及刻。

大家可能留意到镀裤,前三個(gè)函數(shù)都返回一個(gè)取消函數(shù)CancelFunc,這是一個(gè)函數(shù)類型缴饭。

type CancelFunc func()

這就是取消函數(shù)的類型:該函數(shù)可以取消一個(gè)Context暑劝,以及這個(gè)節(jié)點(diǎn)Context下所有的所有的Context,不管有多少層級(jí)颗搂。


WithValue傳遞元數(shù)據(jù)

通過(guò)Context我們也可以傳遞一些必須的元數(shù)據(jù)担猛,這些數(shù)據(jù)會(huì)附加在Context上以供使用。

var key string="name"

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    //附加值
    valueCtx:=context.WithValue(ctx,key,"【監(jiān)控1】")
    go watch(valueCtx)
    time.Sleep(10 * time.Second)
    fmt.Println("可以了丢氢,通知監(jiān)控停止")
    cancel()
    //為了檢測(cè)監(jiān)控過(guò)是否停止傅联,如果沒(méi)有監(jiān)控輸出,就表示停止了
    time.Sleep(5 * time.Second)
}

func watch(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            //取出值
            fmt.Println(ctx.Value(key),"監(jiān)控退出疚察,停止了...")
            return
        default:
            //取出值
            fmt.Println(ctx.Value(key),"goroutine監(jiān)控中...")
            time.Sleep(2 * time.Second)
        }
    }
}
  • 在前面的例子蒸走,我們通過(guò)傳遞參數(shù)的方式,把name的值傳遞給監(jiān)控函數(shù)貌嫡。在這個(gè)例子里比驻,我們實(shí)現(xiàn)一樣的效果该溯,但是通過(guò)的是Context的Value的方式。
  • 我們可以使用context.WithValue方法附加一對(duì)K-V的鍵值對(duì)别惦,這里Key必須是等價(jià)性的狈茉,也就是具有可比性;Value值要是線程安全的掸掸。
  • 這樣我們就生成了一個(gè)新的Context氯庆,這個(gè)新的Context帶有這個(gè)鍵值對(duì),在使用的時(shí)候猾漫,可以通過(guò)Value方法讀取ctx.Value(key)。
  • 記赘蟹铩:使用WithValue傳值悯周,一般是必須的值,不要什么值都傳遞陪竿。

Context 使用原則

  • 不要把Context放在結(jié)構(gòu)體中禽翼,要以參數(shù)的方式傳遞
  • 以Context作為參數(shù)的函數(shù)方法,應(yīng)該把Context作為第一個(gè)參數(shù)族跛,放在第一位闰挡。
  • 給一個(gè)函數(shù)方法傳遞Context的時(shí)候,不要傳遞nil礁哄。如果不知道傳遞什么长酗,就使用context.TODO
  • Context的Value相關(guān)方法應(yīng)該傳遞必須的數(shù)據(jù),不要什么數(shù)據(jù)都使用這個(gè)傳遞
  • Context是線程安全的桐绒,可以放心的在多個(gè)goroutine中傳遞

Context總結(jié):

  • 所有的context的父對(duì)象夺脾,也叫根對(duì)象。是一個(gè)空的context茉继,它不能被取消咧叭,它沒(méi)有值,從不會(huì)被取消烁竭,也沒(méi)有超時(shí)時(shí)間菲茬,它常常作為處理request的頂層context存在,然后通過(guò)WithCancel派撕、WithTimeout函數(shù)來(lái)創(chuàng)建子對(duì)象來(lái)獲得cancel(取消)婉弹、timeout(超時(shí))的能力
  • 當(dāng)頂層的request請(qǐng)求函數(shù)結(jié)束后,我們就可以cancel掉某個(gè)context终吼,從而通知?jiǎng)e的routine結(jié)束
  • WithValue方法可以把鍵值對(duì)加入context中马胧,讓不同的goroutine獲取。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末衔峰,一起剝皮案震驚了整個(gè)濱河市佩脊,隨后出現(xiàn)的幾起案子蛙粘,更是在濱河造成了極大的恐慌,老刑警劉巖威彰,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件出牧,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡歇盼,警方通過(guò)查閱死者的電腦和手機(jī)舔痕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)豹缀,“玉大人伯复,你說(shuō)我怎么就攤上這事⌒象希” “怎么了啸如?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)氮惯。 經(jīng)常有香客問(wèn)我叮雳,道長(zhǎng),這世上最難降的妖魔是什么妇汗? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任帘不,我火速辦了婚禮,結(jié)果婚禮上杨箭,老公的妹妹穿的比我還像新娘寞焙。我一直安慰自己,他們只是感情好互婿,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布棺弊。 她就那樣靜靜地躺著,像睡著了一般擒悬。 火紅的嫁衣襯著肌膚如雪模她。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天懂牧,我揣著相機(jī)與錄音侈净,去河邊找鬼齐婴。 笑死唐含,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的掐场。 我是一名探鬼主播躯保,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼旋膳,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了途事?” 一聲冷哼從身側(cè)響起验懊,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤擅羞,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后义图,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體减俏,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年碱工,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了娃承。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡怕篷,死狀恐怖历筝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情廊谓,我是刑警寧澤梳猪,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站蹂析,受9級(jí)特大地震影響舔示,放射性物質(zhì)發(fā)生泄漏碟婆。R本人自食惡果不足惜电抚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望竖共。 院中可真熱鬧蝙叛,春花似錦、人聲如沸公给。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)淌铐。三九已至肺然,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間腿准,已是汗流浹背际起。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吐葱,地道東北人街望。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像弟跑,于是被迫代替她去往敵國(guó)和親灾前。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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

  • 控制并發(fā)有兩種經(jīng)典的方式孟辑,一種是WaitGroup哎甲,另外一種就是Context蔫敲,今天我就談?wù)凜ontext。 什么...
    豆瓣奶茶閱讀 5,052評(píng)論 4 13
  • [TOC] Golang Context分析 Context背景 和 適用場(chǎng)景 golang在1.6.2的時(shí)候還沒(méi)...
    AllenWu閱讀 11,514評(píng)論 0 30
  • 這是16年5月份編輯的一份比較雜亂適合自己觀看的學(xué)習(xí)記錄文檔烧给,今天18年5月份再次想寫文章燕偶,發(fā)現(xiàn)簡(jiǎn)書(shū)還為我保存起的...
    Jenaral閱讀 2,732評(píng)論 2 9
  • 在夢(mèng)里 你看了我一眼 帶著無(wú)限的眷戀 無(wú)限的感傷 又有著 無(wú)限的渴望 所有的所有的 都在你的眼眸里 我 潸然淚下 ...
    荊芥_f5dd閱讀 149評(píng)論 0 3
  • 直到鬧鈴響畢,借著天花板上木制風(fēng)扇的幫助础嫡,我意識(shí)到我在哪以及在正在這里做什么指么。至于我為什么會(huì)在這而不是在別處,起床...
    馬不虎閱讀 217評(píng)論 0 1