<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Golang Context 的核心實現(xiàn)

          共 1952字,需瀏覽 4分鐘

           ·

          2020-08-16 09:10

          1.基礎(chǔ)筑基

          1.1 基于線程的編程語言中的一些設(shè)計

          1.1.1 ThreadGroup

          ThreadGroup是基于線程并發(fā)的編程語言中常用的一個概念,當(dāng)一個線程派生出一個子線程后通常會加入父線程的線程組(未指定線程組的情況下)中, 最后可以通過ThreadGroup來控制一組線程的退出等操作, 然后在go語言中g(shù)oroutine沒有明確的這種parent/children的關(guān)系,如果想退出當(dāng)前調(diào)用鏈上的所有g(shù)oroutine則需要用到context

          1.1.2 ThreadLocal

          在基于線程的編程語言語言中,通??梢曰赥hreadLocal來進行一些線程本地的存儲,本質(zhì)上是通過一個Map來進行key/value的存儲,而在go里面并沒有ThreadLocal的設(shè)計,在key/value傳遞的時候,除了通過參數(shù)來進行傳遞,也可以通過context來進行上下文信息的傳遞

          1.2 context典型應(yīng)用場景

          場景實現(xiàn)原理?
          上下文信息傳遞WithValue通過一個內(nèi)部的key/value屬性來進行鍵值對的保存,不可修改,只能通過覆蓋的方式來進行值得替換?
          退出通知WithCancel通過監(jiān)聽通知的channel來進行共同退出的通知?

          1.3 上下文數(shù)據(jù)的遞歸獲取

          因為在go的context里面并沒有使用map進行數(shù)據(jù)保存,所以實際獲取的時候,是從當(dāng)前層開始逐層的進行向上遞歸,直至找到某個匹配的key

          其實我們類比ThreadGroup,因為goroutine本身并沒有上下級的概念,但其實我們可以通過context來實現(xiàn)傳遞數(shù)據(jù)的父子關(guān)系,可以在一個goroutine中設(shè)定context數(shù)據(jù),然后傳遞給派生出來的goroutine

          1.4 取消的通知

          既然通過context來構(gòu)建parent/child的父子關(guān)系,在實現(xiàn)的過程中context會向parent來注冊自身,當(dāng)我們?nèi)∠硞€parent的goroutine, 實際上上會遞歸層層cancel掉自己的child context的done chan從而讓整個調(diào)用鏈中所有監(jiān)聽cancel的goroutine退出

          那如果一個child context的done chan為被初始化呢?那怎么通知關(guān)閉呢,那直接給你一個closedchan已經(jīng)關(guān)閉的channel那是不是就可以了呢

          1.5 帶有超時context

          如果要實現(xiàn)一個超時控制,通過上面的context的parent/child機制,其實我們只需要啟動一個定時器,然后在超時的時候,直接將當(dāng)前的context給cancel掉,就可以實現(xiàn)監(jiān)聽在當(dāng)前和下層的額context.Done()的goroutine的退出

          1.6 Background與TODO

          Backgroud其實從字面意思就很容易理解,其實構(gòu)建一個context對象作為root對象,其本質(zhì)上是一個共享的全局變量,通常在一些系統(tǒng)處理中,我們都可以使用該對象作為root對象,并進行新context的構(gòu)建來進行上下文數(shù)據(jù)的傳遞和統(tǒng)一的退出控制

          那TODO呢?通常我們會給自己立很多的todo list,其實這里也一樣,我們雖然構(gòu)建了很多的todo list, 但大多數(shù)人其實啥也不會做,在很多的函數(shù)調(diào)用的過程中都會傳遞但是通常又不會使用,比如你既不會監(jiān)聽退出,也不會從里面獲取數(shù)據(jù),TODO跟Background一樣,其背后也是返回一個全局變量

          1.7 不可變性

          通常我們使用context都是做位一個上下文的數(shù)據(jù)傳遞,比如一次http request請求的處理,但是如果當(dāng)這次請求處理完成,其context就失去了意義,后續(xù)不應(yīng)該繼續(xù)重復(fù)使用一個context, 之前如果超時或者已經(jīng)取消,則其狀態(tài)不會發(fā)生改變

          2. 源碼實現(xiàn)

          2.1 context接口

          type Context interface {    // Deadline返回一個到期的timer定時器,以及當(dāng)前是否以及到期    Deadline() (deadline time.Time, ok bool)
          // Done在當(dāng)前上下文完成后返回一個關(guān)閉的通道,代表當(dāng)前context應(yīng)該被取消,以便goroutine進行清理工作 // WithCancel:負(fù)責(zé)在cancel被調(diào)用的時候關(guān)閉Done // WithDeadline: 負(fù)責(zé)在最后其期限過期時關(guān)閉Done // WithTimeout:負(fù)責(zé)超時后關(guān)閉done Done() <-chan struct{}
          // 如果Done通道沒有被關(guān)閉則返回nil // 否則則會返回一個具體的錯誤 // Canceled 被取消 // DeadlineExceeded 過期 Err() error // 返回對應(yīng)key的value Value(key interface{}) interface{}}

          2.2 emptyCtx

          emptyCtx是一個不會被取消、沒有到期時間、沒有值、不會返回錯誤的context實現(xiàn),其主要作為context.Background()和context.TODO()返回這種root context或者不做任何操作的context

          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}func (e *emptyCtx) String() string { switch e { case background: return "context.Background" case todo: return "context.TODO" } return "unknown empty Context"}

          比較有意思的實現(xiàn)時emptyCtx的String方法,該方法可以返回當(dāng)前context的具體類型,比如是Background還是TODO, 因為background和todo是兩個全局變量,這里通過取其地址來進行對應(yīng)類型的判斷

          2.3 cancelCtx

          2.3.1 結(jié)構(gòu)體

          cancelCtx結(jié)構(gòu)體內(nèi)嵌了一個Context對象,即其parent context,同時內(nèi)部還通過children來保存所有可以被取消的context的接口,后續(xù)當(dāng)當(dāng)前context被取消的時候,只需要調(diào)用所有canceler接口的context就可以實現(xiàn)當(dāng)前調(diào)用鏈的取消

          type cancelCtx struct {    Context
          mu sync.Mutex // protects following fields 保護屬性 done chan struct{} // created lazily, closed by first cancel call children map[canceler]struct{} // set to nil by the first cancel call err error // set to non-nil by the first cancel call}

          2.3.2 Done

          Done操作返回當(dāng)前的一個chan 用于通知goroutine退出

          func (c *cancelCtx) Done() <-chan struct{} {    c.mu.Lock()    if c.done == nil {        c.done = make(chan struct{})    }    d := c.done    c.mu.Unlock()    return d}

          2.3.3 cancel

          func (c *cancelCtx) cancel(removeFromParent bool, err error) {    if err == nil {        panic("context: internal error: missing cancel error")    }    // context一旦被某個操作操作觸發(fā)取消后,就不會在進行任何狀態(tài)的修改    c.mu.Lock()    if c.err != nil {        c.mu.Unlock()        return // already canceled    }    c.err = err    if c.done == nil {        c.done = closedchan    } else {        // close當(dāng)前chan        close(c.done)    }    // 調(diào)用所有children取消    for child := range c.children {        child.cancel(false, err)    }    c.children = nil    c.mu.Unlock()
          // 是否需要從parent context中移除,如果是當(dāng)前context的取消操作,則需要進行該操作 // 否則,則上層context會主動進行child的移除工作 if removeFromParent { removeChild(c.Context, c) }}

          2.4 timerCtx

          timerCtx主要是用于實現(xiàn)WithDeadline和WithTimer兩個context實現(xiàn),其繼承了cancelCtx接口,同時還包含一個timer.Timer定時器和一個deadline終止實現(xiàn)

          2.4.1 結(jié)構(gòu)體

          timerCtx

          type timerCtx struct {    cancelCtx    timer *time.Timer // timer定時器
          deadline time.Time //終止時間}

          2.4.2 取消方法

          取消方法就很簡單了首先進行cancelCtx的取消流程,然后進行自身的定時器的Stop操作,這樣就可以實現(xiàn)取消了

          func (c *timerCtx) cancel(removeFromParent bool, err error) {    c.cancelCtx.cancel(false, err)    if removeFromParent {        // Remove this timerCtx from its parent cancelCtx's children.        removeChild(c.cancelCtx.Context, c)    }    c.mu.Lock()    if c.timer != nil {        c.timer.Stop() // 停止定時器        c.timer = nil    }    c.mu.Unlock()}

          2.5 valueCtx

          其內(nèi)部通過一個key/value進行值得保存,如果當(dāng)前context不包含著值就會層層向上遞歸

          type valueCtx struct {    Context    key, val interface{}}
          func (c *valueCtx) String() string { return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)}
          func (c *valueCtx) Value(key interface{}) interface{} { if c.key == key { return c.val } return c.Context.Value(key)}

          2.6 propagateCancel

          2.6.1 設(shè)計目標(biāo)

          propagateCancel主要設(shè)計目標(biāo)就是當(dāng)parent context取消的時候,進行child context的取消, 這就會有兩種模式:1.parent取消的時候通知child進行cancel取消2.parent取消的時候調(diào)用child的層層遞歸取消

          2.6.2 parentCancelCtx

          context可以任意嵌套組成一個N層樹形結(jié)構(gòu)的context, 結(jié)合上面的兩種模式,當(dāng)能找到parent為cancelCtx、timerCtx任意一種的時候,就采用第二種模式,由parent來調(diào)用child的cancel完成整個調(diào)用鏈的退出,反之則采用第一種模式監(jiān)聽Done

          func parentCancelCtx(parent Context) (*cancelCtx, bool) {    for {        switch c := parent.(type) {        case *cancelCtx:            return c, true    // 找到最近支持cancel的parent,由parent進行取消操作的調(diào)用        case *timerCtx:            return &c.cancelCtx, true // 找到最近支持cancel的parent,由parent進行取消操作的調(diào)用        case *valueCtx:            parent = c.Context // 遞歸        default:            return nil, false        }    }}

          2.6.3 核心實現(xiàn)

          func propagateCancel(parent Context, child canceler) {    if parent.Done() == nil {        return // parent is never canceled    }    if p, ok := parentCancelCtx(parent); ok {        p.mu.Lock()        if p.err != nil {            // parent has already been canceled            // 如果發(fā)現(xiàn)parent已經(jīng)取消就直接進行取消            child.cancel(false, p.err)        } else {            if p.children == nil {                p.children = make(map[canceler]struct{})            }            // 否則加入parent的children map中            p.children[child] = struct{}{}        }        p.mu.Unlock()    } else {        go func() {            select {            case <-parent.Done():                // 監(jiān)聽parent DOne完成, 此處也不會向parent進行注冊                child.cancel(false, parent.Err())            case <-child.Done():            }        }()    }}

          2.7 WithDeadline

          有了上面的基礎(chǔ)學(xué)習(xí)WithDeadline,就簡單了許多, WithDeadline會給定一個截止時間, 可以通過當(dāng)前時間計算需要等待多長時間取消即可

          func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {    if cur, ok := parent.Deadline(); ok && cur.Before(d) {        // The current deadline is already sooner than the new one.        return WithCancel(parent)    }    c := &timerCtx{        cancelCtx: newCancelCtx(parent),        deadline:  d,    }    // 監(jiān)聽parent的取消,或者向parent注冊自身    propagateCancel(parent, c)    dur := time.Until(d)    if dur <= 0 {        // 已經(jīng)過期        c.cancel(true, DeadlineExceeded) // deadline has already passed        return c, func() { c.cancel(false, Canceled) }    }    c.mu.Lock()    defer c.mu.Unlock()    if c.err == nil {        c.timer = time.AfterFunc(dur, func() {            // 構(gòu)建一個timer定時器,到期后自動調(diào)用cancel取消            c.cancel(true, DeadlineExceeded)        })    }    // 返回取消函數(shù)    return c, func() { c.cancel(true, Canceled) }}

          2.8 Backgroup與TODO

          在很多底層的中間件的調(diào)用中都會通過context進行信息的傳遞,其中最常用的就是Backgroup和Todo, 雖然都是基于emptyCtx實現(xiàn),但Backgroup則更傾向于作為一個parent context進行后續(xù)整個調(diào)用鏈context的root使用,而TODO通常則表明后續(xù)不會進行任何操作,僅僅是因為參數(shù)需要傳遞使用。



          K8S進階訓(xùn)練營,點擊下方圖片了解詳情


          瀏覽 45
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  豆花视频理论在线播放 | 小骚逼操死你视频 | 五月丁香最新网址导航 | 一级黄色毛片免费 | 男女操逼拍小视频 |