在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

context 底層原理介紹

馬哥Linux運(yùn)維 ? 來(lái)源:馬哥Linux運(yùn)維 ? 作者:馬哥Linux運(yùn)維 ? 2022-11-05 12:14 ? 次閱讀

1. context 介紹

很多時(shí)候,我們會(huì)遇到這樣的情況,上層與下層的goroutine需要同時(shí)取消,這樣就涉及到了goroutine間的通信。在Go中,推薦我們以通信的方式共享內(nèi)存,而不是以共享內(nèi)存的方式通信。

所以,就需要用到channl,但是,在上述場(chǎng)景中,如果需要自己去處理channl的業(yè)務(wù)邏輯,就會(huì)有很多費(fèi)時(shí)費(fèi)力的重復(fù)工作,因此,context出現(xiàn)了。

context是Go中用來(lái)進(jìn)程通信的一種方式,其底層是借助channl與snyc.Mutex實(shí)現(xiàn)的。

2. 基本介紹

context的底層設(shè)計(jì),我們可以概括為1個(gè)接口,4種實(shí)現(xiàn)與6個(gè)方法。

1 個(gè)接口

Context 規(guī)定了context的四個(gè)基本方法

4 種實(shí)現(xiàn)

emptyCtx 實(shí)現(xiàn)了一個(gè)空的context,可以用作根節(jié)點(diǎn)

cancelCtx 實(shí)現(xiàn)一個(gè)帶cancel功能的context,可以主動(dòng)取消

timerCtx 實(shí)現(xiàn)一個(gè)通過(guò)定時(shí)器timer和截止時(shí)間deadline定時(shí)取消的context

valueCtx 實(shí)現(xiàn)一個(gè)可以通過(guò) key、val 兩個(gè)字段來(lái)存數(shù)據(jù)的context

6 個(gè)方法

Background 返回一個(gè)emptyCtx作為根節(jié)點(diǎn)

TODO 返回一個(gè)emptyCtx作為未知節(jié)點(diǎn)

WithCancel 返回一個(gè)cancelCtx

WithDeadline 返回一個(gè)timerCtx

WithTimeout 返回一個(gè)timerCtx

WithValue 返回一個(gè)valueCtx

3. 源碼分析

3.1 Context 接口

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


Deadline() :返回一個(gè)time.Time,表示當(dāng)前Context應(yīng)該結(jié)束的時(shí)間,ok則表示有結(jié)束時(shí)間

Done():返回一個(gè)只讀chan,如果可以從該 chan 中讀取到數(shù)據(jù),則說(shuō)明 ctx 被取消了

Err():返回 Context 被取消的原因

Value(key):返回key對(duì)應(yīng)的value,是協(xié)程安全的

3.2 emptyCtx


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接口,其主要作用是為Background和TODO這兩個(gè)方法都會(huì)返回預(yù)先初始化好的私有變量background和todo,它們會(huì)在同一個(gè) Go 程序中被復(fù)用:

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


func Background() Context {
    return background
}
func TODO() Context {
 return todo
}

Background和TODO在實(shí)現(xiàn)上沒(méi)有區(qū)別,只是在使用語(yǔ)義上有所差異:

Background是上下文的根節(jié)點(diǎn);

TODO應(yīng)該僅在不確定應(yīng)該使用哪種上下文時(shí)使用;

3.3 cancelCtx

cancelCtx實(shí)現(xiàn)了canceler接口與Context接口:
type canceler interface {
  cancel(removeFromParent bool, err error)
  Done() <-chan struct{}
}

其結(jié)構(gòu)體如下:

type cancelCtx struct {
    // 直接嵌入了一個(gè) Context,那么可以把 cancelCtx 看做是一個(gè) Context
 Context


 mu       sync.Mutex            // protects following fields
 done     atomic.Value          // of 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
}

我們可以使用WithCancel的方法來(lái)創(chuàng)建一個(gè)cancelCtx:

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
 if parent == nil {
  panic("cannot create context from nil parent")
 }
 c := newCancelCtx(parent)
 propagateCancel(parent, &c)
 return &c, func() { c.cancel(true, Canceled) }
}
func newCancelCtx(parent Context) cancelCtx {
 return cancelCtx{Context: parent}
}

上面的方法,我們傳入一個(gè)父 Context(這通常是一個(gè) background,作為根節(jié)點(diǎn)),返回新建的 context,并通過(guò)閉包的形式,返回了一個(gè) cancel 方法。

newCancelCtx將傳入的上下文包裝成私有結(jié)構(gòu)體context.cancelCtx。

propagateCancel則會(huì)構(gòu)建父子上下文之間的關(guān)聯(lián),形成樹(shù)結(jié)構(gòu),當(dāng)父上下文被取消時(shí),子上下文也會(huì)被取消:

func propagateCancel(parent Context, child canceler) {
    // 1.如果 parent ctx 是不可取消的 ctx,則直接返回 不進(jìn)行關(guān)聯(lián)
 done := parent.Done()
 if done == nil {
  return // parent is never canceled
 }
    // 2.接著判斷一下 父ctx 是否已經(jīng)被取消
 select {
 case <-done:
        // 2.1 如果 父ctx 已經(jīng)被取消了,那就沒(méi)必要關(guān)聯(lián)了
        // 然后這里也要順便把子ctx給取消了,因?yàn)楦竎tx取消了 子ctx就應(yīng)該被取消
        // 這里是因?yàn)檫€沒(méi)有關(guān)聯(lián)上,所以需要手動(dòng)觸發(fā)取消
  // parent is already canceled
  child.cancel(false, parent.Err())
  return
 default:
 }
    // 3. 從父 ctx 中提取出 cancelCtx 并將子ctx加入到父ctx 的 children 里面
 if p, ok := parentCancelCtx(parent); ok {
  p.mu.Lock()
        // double check 一下,確認(rèn)父 ctx 是否被取消
  if p.err != nil {
            // 取消了就直接把當(dāng)前這個(gè)子ctx給取消了
   // parent has already been canceled
   child.cancel(false, p.err)
  } else {
            // 否則就添加到 children 里面
   if p.children == nil {
    p.children = make(map[canceler]struct{})
   }
   p.children[child] = struct{}{}
  }
  p.mu.Unlock()
 } else {
        // 如果沒(méi)有找到可取消的父 context。新啟動(dòng)一個(gè)協(xié)程監(jiān)控父節(jié)點(diǎn)或子節(jié)點(diǎn)取消信號(hào)
  atomic.AddInt32(&goroutines, +1)
  go func() {
   select {
   case <-parent.Done():
    child.cancel(false, parent.Err())
   case <-child.Done():
   }
  }()
 }
}

上面的方法可能遇到以下幾種情況:

當(dāng) parent.Done() == nil,也就是 parent 不會(huì)觸發(fā)取消事件時(shí),當(dāng)前函數(shù)會(huì)直接返回;

當(dāng) child 的繼承鏈包含可以取消的上下文時(shí),會(huì)判斷 parent 是否已經(jīng)觸發(fā)了取消信號(hào);

如果已經(jīng)被取消,child 會(huì)立刻被取消;

如果沒(méi)有被取消,child 會(huì)被加入 parent 的 children 列表中,等待 parent 釋放取消信號(hào);

當(dāng)父上下文是開(kāi)發(fā)者自定義的類(lèi)型、實(shí)現(xiàn)了 context.Context 接口并在 Done()方法中返回了非空的管道時(shí);

運(yùn)行一個(gè)新的 Goroutine 同時(shí)監(jiān)聽(tīng) parent.Done()和 child.Done()兩個(gè) Channel;

在 parent.Done()關(guān)閉時(shí)調(diào)用 child.cancel 取消子上下文;

propagateCancel 的作用是在 parent 和 child 之間同步取消和結(jié)束的信號(hào),保證在 parent 被取消時(shí),child 也會(huì)收到對(duì)應(yīng)的信號(hào),不會(huì)出現(xiàn)狀態(tài)不一致的情況。


func parentCancelCtx(parent Context) (*cancelCtx, bool) {
 done := parent.Done()
    // 如果 done 為 nil 說(shuō)明這個(gè)ctx是不可取消的
    // 如果 done == closedchan 說(shuō)明這個(gè)ctx不是標(biāo)準(zhǔn)的 cancelCtx,可能是自定義的
 if  done == closedchan || done == nil {
  return nil, false
 }
    // 然后調(diào)用 value 方法從ctx中提取出 cancelCtx
 p, ok := parent.Value(&cancelCtxKey).(*cancelCtx)
 if !ok {
  return nil, false
 }
    // 最后再判斷一下cancelCtx 里存的 done 和 父ctx里的done是否一致
    // 如果不一致說(shuō)明parent不是一個(gè) cancelCtx
 pdone, _ := p.done.Load().(chan struct{})
 if pdone != done {
  return nil, false
 }
 return p, true
}






ancelCtx 的 done 方法會(huì)返回一個(gè) chan struct{}:
func (c *cancelCtx) Done() <-chan struct{} {
 d := c.done.Load()
 if d != nil {
  return d.(chan struct{})
 }
 c.mu.Lock()
 defer c.mu.Unlock()
 d = c.done.Load()
 if d == nil {
  d = make(chan struct{})
  c.done.Store(d)
 }
 return d.(chan struct{})
}
var closedchan = make(chan struct{})

parentCancelCtx 其實(shí)就是判斷 parent context 里面有沒(méi)有一個(gè) cancelCtx,有就返回,讓子context可以“掛靠”到parent context 上,如果不是就返回false,不進(jìn)行掛靠,自己新開(kāi)一個(gè) goroutine 來(lái)監(jiān)聽(tīng)。

3.4 timerCtx

timerCtx 內(nèi)部不僅通過(guò)嵌入 cancelCtx 的方式承了相關(guān)的變量和方法,還通過(guò)持有的定時(shí)器 timer 和截止時(shí)間 deadline 實(shí)現(xiàn)了定時(shí)取消的功能:

type timerCtx struct {
 cancelCtx
 timer *time.Timer // Under cancelCtx.mu.


 deadline time.Time
}


func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
 return c.deadline, true
}


func (c *timerCtx) cancel(removeFromParent bool, err error) {
 c.cancelCtx.cancel(false, err)
 if removeFromParent {
  removeChild(c.cancelCtx.Context, c)
 }
 c.mu.Lock()
 if c.timer != nil {
  c.timer.Stop()
  c.timer = nil
 }
 c.mu.Unlock()
}

3.5 valueCtx

valueCtx 是多了 key、val 兩個(gè)字段來(lái)存數(shù)據(jù):


type valueCtx struct {
  Context
  key, val interface{}
}

取值查找的過(guò)程,實(shí)際上是一個(gè)遞歸查找的過(guò)程:


func (c *valueCtx) Value(key interface{}) interface{} {
 if c.key == key {
  return c.val
 }
 return c.Context.Value(key)
}

如果 key 和當(dāng)前 ctx 中存的 value 一致就直接返回,沒(méi)有就去 parent 中找。最終找到根節(jié)點(diǎn)(一般是 emptyCtx),直接返回一個(gè) nil。所以用 Value 方法的時(shí)候要判斷結(jié)果是否為 nil,類(lèi)似于一個(gè)鏈表,效率是很低的,不建議用來(lái)傳參數(shù)。

4. 使用建議

在官方博客里,對(duì)于使用 context 提出了幾點(diǎn)建議:

不要將 Context 塞到結(jié)構(gòu)體里。直接將 Context 類(lèi)型作為函數(shù)的第一參數(shù),而且一般都命名為 ctx

不要向函數(shù)傳入一個(gè) nil 的 context,如果你實(shí)在不知道傳什么,標(biāo)準(zhǔn)庫(kù)給你準(zhǔn)備好了一個(gè) context:todo。

不要把本應(yīng)該作為函數(shù)參數(shù)的類(lèi)型塞到 context 中,context 存儲(chǔ)的應(yīng)該是一些共同的數(shù)據(jù)。例如:登陸的 session、cookie 等。

同一個(gè) context 可能會(huì)被傳遞到多個(gè) goroutine,別擔(dān)心,context 是并發(fā)安全的。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8667

    瀏覽量

    151523
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    7102

    瀏覽量

    89277
  • 通信
    +關(guān)注

    關(guān)注

    18

    文章

    6046

    瀏覽量

    136215

原文標(biāo)題:Go 語(yǔ)言上下文 context 底層原理

文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    鴻蒙開(kāi)發(fā)接口Ability框架:【 (Context模塊)】

    Context模塊提供了ability或application的上下文的能力,包括允許訪問(wèn)特定于應(yīng)用程序的資源、請(qǐng)求和驗(yàn)證權(quán)限等。
    的頭像 發(fā)表于 05-13 16:04 ?737次閱讀
    鴻蒙開(kāi)發(fā)接口Ability框架:【 (<b class='flag-5'>Context</b>模塊)】

    鴻蒙開(kāi)發(fā)接口Ability框架:【Context

    Context模塊提供開(kāi)發(fā)者運(yùn)行代碼的上下文環(huán)境的能力,包括查詢(xún)和設(shè)置應(yīng)用信息、ResourceManager等信息。
    的頭像 發(fā)表于 05-21 17:33 ?896次閱讀
    鴻蒙開(kāi)發(fā)接口Ability框架:【<b class='flag-5'>Context</b>】

    進(jìn)程Context定義

    當(dāng)一個(gè)進(jìn)程在執(zhí)行時(shí), CPU的所有寄存器中的值、進(jìn)程的狀態(tài)以及堆棧中的內(nèi)容,比如各個(gè)變量和數(shù)據(jù),包括所有的寄存器變量、進(jìn)程打開(kāi)的文件、內(nèi)存信息等。這些信息被稱(chēng)為該進(jìn)程的上下文(Context).
    發(fā)表于 08-05 07:11

    進(jìn)程的Context定義

    當(dāng)一個(gè)進(jìn)程在執(zhí)行時(shí), CPU的所有寄存器中的值、進(jìn)程的狀態(tài)以及堆棧中的內(nèi)容,比如各個(gè)變量和數(shù)據(jù),包括所有的寄存器變量、進(jìn)程打開(kāi)的文件、內(nèi)存信息等。這些信息被稱(chēng)為該進(jìn)程的上下文(Context).
    發(fā)表于 08-07 08:20

    基于底層硬體的軟件設(shè)計(jì)

    本內(nèi)容介紹了基于底層硬體的軟件設(shè)計(jì)
    發(fā)表于 05-09 16:04 ?60次下載
    基于<b class='flag-5'>底層</b>硬體的軟件設(shè)計(jì)

    關(guān)于高阻態(tài)和OOC(out of context)綜合方式

    Xilinx Vivado工具支持僅將系統(tǒng)設(shè)計(jì)的一部分進(jìn)行綜合,即OOC(out of context)綜合方式。OOC綜合方式的流程就是將設(shè)計(jì)的某個(gè)模塊單獨(dú)完成綜合操作,這會(huì)帶來(lái)如下可能性
    發(fā)表于 03-21 09:50 ?5808次閱讀

    Redis基本類(lèi)型和底層實(shí)現(xiàn)

    簡(jiǎn)單介紹了Redis的五種對(duì)象類(lèi)型和它們的底層實(shí)現(xiàn)。事實(shí)上,Redis的高效性和靈活性正是得益于對(duì)于同一個(gè)對(duì)象類(lèi)型采取不同的底層結(jié)構(gòu),并在必要的時(shí)候?qū)Χ哌M(jìn)行轉(zhuǎn)換;以及各種底層結(jié)構(gòu)對(duì)內(nèi)
    發(fā)表于 11-25 15:11 ?4490次閱讀
    Redis基本類(lèi)型和<b class='flag-5'>底層</b>實(shí)現(xiàn)

    TiDB底層存儲(chǔ)結(jié)構(gòu)LSM樹(shù)原理介紹

    隨著數(shù)據(jù)量的增大,傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)越來(lái)越不能滿足對(duì)于海量數(shù)據(jù)存儲(chǔ)的需求。對(duì)于分布式關(guān)系型數(shù)據(jù)庫(kù),我們了解其底層存儲(chǔ)結(jié)構(gòu)是非常重要的。本文將介紹下分布式關(guān)系型數(shù)據(jù)庫(kù) TiDB 所采用的底層存儲(chǔ)結(jié)構(gòu) LSM 樹(shù)的原理。
    的頭像 發(fā)表于 01-13 10:00 ?1014次閱讀

    In-context learning介紹

    隨著大規(guī)模預(yù)訓(xùn)練語(yǔ)言模型(LLM)能力的不斷提升,in-context learning(ICL)逐漸成為自然語(yǔ)言處理領(lǐng)域一個(gè)新的范式。
    的頭像 發(fā)表于 04-21 10:02 ?1690次閱讀

    介紹下volatile的底層原理

    線程安全的三大特性,原子性、可見(jiàn)性、有序性,這三大特性與我們之前整理的內(nèi)容息息相關(guān)。本篇重點(diǎn)介紹下volatile的底層原理,幫助我們更好的理解java并發(fā)包。
    的頭像 發(fā)表于 06-09 16:17 ?885次閱讀
    <b class='flag-5'>介紹</b>下volatile的<b class='flag-5'>底層</b>原理

    In-Context-Learning在更大的語(yǔ)言模型上表現(xiàn)不同

    最近,在語(yǔ)言模型領(lǐng)域取得了巨大的進(jìn)展,部分是因?yàn)樗鼈兛梢酝ㄟ^(guò)In-Context- Learning ( ICL)來(lái)完 成各種復(fù)雜的任務(wù)。
    的頭像 發(fā)表于 06-12 14:35 ?733次閱讀
    In-<b class='flag-5'>Context</b>-Learning在更大的語(yǔ)言模型上表現(xiàn)不同

    關(guān)于GO CONTEXT機(jī)制實(shí)現(xiàn)原則

    Context接口沒(méi)有提供方法來(lái)設(shè)置其值和過(guò)期時(shí)間,也沒(méi)有提供方法直接將其自身撤銷(xiāo)。也就是說(shuō),Context不能改變和撤銷(xiāo)其自身。那么該怎么通過(guò)Context傳遞改變后的狀態(tài)呢?
    的頭像 發(fā)表于 11-16 16:15 ?376次閱讀

    鴻蒙開(kāi)發(fā)接口Ability框架:【Context

    Context模塊提供開(kāi)發(fā)者運(yùn)行代碼的上下文環(huán)境的能力,包括查詢(xún)和設(shè)置應(yīng)用信息、ResourceManager等信息。
    的頭像 發(fā)表于 05-15 15:29 ?589次閱讀
    鴻蒙開(kāi)發(fā)接口Ability框架:【<b class='flag-5'>Context</b>】

    鴻蒙Ability Kit(程序框架服務(wù))【應(yīng)用上下文Context

    [Context]是應(yīng)用中對(duì)象的上下文,其提供了應(yīng)用的一些基礎(chǔ)信息,例如resourceManager(資源管理)、applicationInfo(當(dāng)前應(yīng)用信息)、dir(應(yīng)用文件路徑)、area
    的頭像 發(fā)表于 06-06 09:22 ?523次閱讀
    鴻蒙Ability Kit(程序框架服務(wù))【應(yīng)用上下文<b class='flag-5'>Context</b>】

    鴻蒙開(kāi)發(fā)組件:FA模型的Context

    FA模型下只有一個(gè)Context。Context中的所有功能都是通過(guò)方法來(lái)提供的,它提供了一些featureAbility中不存在的方法,相當(dāng)于featureAbility的一個(gè)擴(kuò)展和補(bǔ)全。
    的頭像 發(fā)表于 06-21 09:43 ?320次閱讀
    鴻蒙開(kāi)發(fā)組件:FA模型的<b class='flag-5'>Context</b>
    主站蜘蛛池模板: 亚洲淫视频| 桃色婷婷| 欧美爽爽网| 四虎永久在线精品2022| 色综合综合网| 欧美一区二区三区四区在线观看| 日本午夜大片| 久久99久久精品免费思思6| 五月婷婷六月丁香综合| 天堂中文字幕| 色99色| 欧美伦理影院| 国产在线a不卡免费视频| 欧美freesex10一13| 久久综合九九亚洲一区| 国产精品三级| 天天操天天干天天干| 精品久久久久久久久久| 黄色在线看网站| 综合亚洲色图| 日本簧片在线观看| 国产一级一片免费播放视频| 午夜在线一区| 亚洲精品午夜久久aaa级久久久| 天天爽夜夜爽| 国产紧缚jvid| 欧美亚洲在线| 天天狠狠色噜噜| 欧美片欧美日韩国产综合片| 成人三级在线播放线观看| 婷婷网五月天天综合天天爱| 国产成人亚洲影视在线| 亚州一级毛片在线| 五月亭亭免费高清在线| 久久这里只精品热在线8| 在线看免费| 超薄肉色丝袜精品足j福利| 男人午夜天堂| 三级在线观看视频| 高清欧美日本视频免费观看| 大伊人网|