最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

go并發(fā)編程sync.Cond使用場(chǎng)景及實(shí)現(xiàn)原理

 更新時(shí)間:2022年08月31日 11:12:25   作者:Koffee  
這篇文章主要為大家介紹了go并發(fā)編程sync.Cond使用場(chǎng)景及實(shí)現(xiàn)原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

使用場(chǎng)景

sync.Cond是go標(biāo)準(zhǔn)庫(kù)提供的一個(gè)條件變量,用于控制一組goroutine在滿(mǎn)足特定條件下被喚醒。

sync.Cond常用于一組goroutine等待,一個(gè)goroutine通知(事件發(fā)生)的場(chǎng)景。如果只有一個(gè)goroutine等待,一個(gè)goroutine通知(事件發(fā)生),使用Mutex或者Channel就可以實(shí)現(xiàn)。

可以用一個(gè)全局變量標(biāo)志特定條件condition,每個(gè)sync.Cond都必須要關(guān)聯(lián)一個(gè)互斥鎖(Mutex或者RWMutex),當(dāng)condition發(fā)生變更或者調(diào)用Wait時(shí),都必須加鎖,保證多個(gè)goroutine安全地訪(fǎng)問(wèn)condition。

下面是go標(biāo)準(zhǔn)庫(kù)http中關(guān)于pipe的部分實(shí)現(xiàn),我們可以看到,pipe使用sync.Cond來(lái)控制管道中字節(jié)流的寫(xiě)入和讀取,在pipe中數(shù)據(jù)可用并且字節(jié)流復(fù)制到pipe的緩沖區(qū)之前,所有的需要讀取該管道數(shù)據(jù)的goroutine都必須等待,直到數(shù)據(jù)準(zhǔn)備完成。

type pipe struct {
   mu       sync.Mutex
   c        sync.Cond     // c.L lazily initialized to &p.mu
   b        pipeBuffer    // nil when done reading
   ...
}
// Read waits until data is available and copies bytes
// from the buffer into p.
func (p *pipe) Read(d []byte) (n int, err error) {
   p.mu.Lock()
   defer p.mu.Unlock()
   if p.c.L == nil {
      p.c.L = &p.mu
   }
   for {
      ...
      if p.b != nil && p.b.Len() > 0 {
         return p.b.Read(d)
      }
      ...
      p.c.Wait() // write未完成前調(diào)用Wait進(jìn)入等待
   }
}
// Write copies bytes from p into the buffer and wakes a reader.
// It is an error to write more data than the buffer can hold.
func (p *pipe) Write(d []byte) (n int, err error) {
   p.mu.Lock()
   defer p.mu.Unlock()
   if p.c.L == nil {
      p.c.L = &p.mu
   }
   defer p.c.Signal() // 喚醒所有等待的goroutine
   if p.err != nil {
      return 0, errClosedPipeWrite
   }
   if p.breakErr != nil {
      p.unread += len(d)
      return len(d), nil // discard when there is no reader
   }
   return p.b.Write(d)
}

實(shí)現(xiàn)原理

type Cond struct {
   noCopy noCopy       // 用來(lái)保證結(jié)構(gòu)體無(wú)法在編譯期間拷貝
   // L is held while observing or changing the condition
   L Locker             // 用來(lái)保證condition變更安全
   notify  notifyList   // 待通知的goutine列表
   checker copyChecker  // 用于禁止運(yùn)行期間發(fā)生的拷貝
}
type notifyList struct {
   wait   uint32      // 正在等待的goroutine的ticket
   notify uint32      // 已經(jīng)通知到的goroutine的ticket
   lock   uintptr // key field of the mutex
   head   unsafe.Pointer     // 鏈表頭部
   tail   unsafe.Pointer     // 鏈表尾部
}

copyChecker

copyChecker是一個(gè)指針類(lèi)型,在創(chuàng)建時(shí),它的值指向自身地址,用于檢測(cè)該對(duì)象是否發(fā)生了拷貝。如果發(fā)生了拷貝,則直接panic。

// copyChecker holds back pointer to itself to detect object copying.
type copyChecker uintptr
func (c *copyChecker) check() {
   if uintptr(*c) != uintptr(unsafe.Pointer(c)) &&
      !atomic.CompareAndSwapUintptr((*uintptr)(c), 0, uintptr(unsafe.Pointer(c))) &&
      uintptr(*c) != uintptr(unsafe.Pointer(c)) {
      panic("sync.Cond is copied")
   }
}

Wait

調(diào)用 Wait 會(huì)自動(dòng)釋放鎖 c.L,并掛起調(diào)用者所在的 goroutine,因此當(dāng)前協(xié)程會(huì)阻塞在 Wait 方法調(diào)用的地方。如果其他協(xié)程調(diào)用了 Signal 或 Broadcast 喚醒了該協(xié)程,那么 Wait 方法在結(jié)束阻塞時(shí),會(huì)重新給 c.L 加鎖,并且繼續(xù)執(zhí)行 Wait 后面的代碼。

對(duì)條件的檢查,使用了 for !condition() 而非 if,是因?yàn)楫?dāng)前協(xié)程被喚醒時(shí),條件不一定符合要求,需要再次 Wait 等待下次被喚醒。為了保險(xiǎn)起見(jiàn),使用 for 能夠確保條件符合要求后,再執(zhí)行后續(xù)的代碼。

func (c *Cond) Wait() {
   c.checker.check()
   t := runtime_notifyListAdd(&c.notify)
   c.L.Unlock()
   runtime_notifyListWait(&c.notify, t)
   c.L.Lock()
}
  • 檢查Cond是否被復(fù)制,如果被復(fù)制,直接panic;
  • 調(diào)用runtime_notifyListAdd調(diào)用者添加到通知列表并解鎖,以便可以接收到通知,然后將返回的ticket傳入到runtime_notifyListWait來(lái)等待通知。
  • 當(dāng)前goroutine會(huì)阻塞在wait調(diào)用的地方,直到其他goroutine調(diào)用Signal或Broadcast喚醒該協(xié)程。
func notifyListAdd(l *notifyList) uint32 {
    return atomic.Xadd(&l.wait, 1) - 1
}

notifyListWait會(huì)將當(dāng)前goroutine追加到鏈表的尾端,同時(shí)調(diào)用goparkunlock讓當(dāng)前goroutine陷入休眠,該方法會(huì)直接讓出當(dāng)前處理器的使用權(quán)并等待調(diào)度器的喚醒。

func notifyListWait(l *notifyList, t uint32) {
    s := acquireSudog()
    s.g = getg()
    s.ticket = t
    if l.tail == nil {
       l.head = s
    } else {
       l.tail.next = s
    }
    l.tail = s
    goparkunlock(&l.lock, waitReasonSyncCondWait, traceEvGoBlockCond, 3)
    releaseSudog(s)
}

Signal

Signal會(huì)喚醒隊(duì)列最前面的Goroutine。

func (c *Cond) Signal() {
   c.checker.check()
   runtime_notifyListNotifyOne(&c.notify)
}
func notifyListNotifyOne(l *notifyList) {
   t := l.notify
   atomic.Store(&l.notify, t+1)
   for p, s := (*sudog)(nil), l.head; s != nil; p, s = s, s.next {
      if s.ticket == t {
         n := s.next
         if p != nil {
            p.next = n
         } else {
            l.head = n
         }
         if n == nil {
            l.tail = p
         }
         s.next = nil
         readyWithTime(s, 4)
         return
      }
   }
}

Broadcast

Broadcast會(huì)喚醒隊(duì)列中全部的goroutine。

func (c *Cond) Broadcast() {
    c.checker.check()
    runtime_notifyListNotifyAll(&c.notify)
}
func notifyListNotifyAll(l *notifyList) {
   s := l.head
   l.head = nil
   l.tail = nil
   atomic.Store(&l.notify, atomic.Load(&l.wait))
   for s != nil {
      next := s.next
      s.next = nil
      readyWithTime(s, 4)
      s = next
   }
}

以上就是go并發(fā)編程sync.Cond使用場(chǎng)景及實(shí)現(xiàn)原理的詳細(xì)內(nèi)容,更多關(guān)于go并發(fā)編程sync.Cond的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • GoLang中的互斥鎖Mutex和讀寫(xiě)鎖RWMutex使用教程

    GoLang中的互斥鎖Mutex和讀寫(xiě)鎖RWMutex使用教程

    RWMutex是一個(gè)讀/寫(xiě)互斥鎖,在某一時(shí)刻只能由任意數(shù)量的reader持有或者一個(gè)writer持有。也就是說(shuō),要么放行任意數(shù)量的reader,多個(gè)reader可以并行讀;要么放行一個(gè)writer,多個(gè)writer需要串行寫(xiě)
    2023-01-01
  • Go語(yǔ)言轉(zhuǎn)換所有字符串為大寫(xiě)或者小寫(xiě)的方法

    Go語(yǔ)言轉(zhuǎn)換所有字符串為大寫(xiě)或者小寫(xiě)的方法

    這篇文章主要介紹了Go語(yǔ)言轉(zhuǎn)換所有字符串為大寫(xiě)或者小寫(xiě)的方法,實(shí)例分析了ToLower和ToUpper函數(shù)的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • 詳解Go中Map類(lèi)型和Slice類(lèi)型的傳遞

    詳解Go中Map類(lèi)型和Slice類(lèi)型的傳遞

    這篇文章主要為大家詳細(xì)介紹了Go中Map類(lèi)型和Slice類(lèi)型的傳遞,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • 關(guān)于升級(jí)go1.18的goland問(wèn)題詳解

    關(guān)于升級(jí)go1.18的goland問(wèn)題詳解

    作為一個(gè)go語(yǔ)言程序員,覺(jué)得自己有義務(wù)為go新手開(kāi)一條更簡(jiǎn)單便捷的上手之路,下面這篇文章主要給大家介紹了關(guān)于升級(jí)go1.18的goland問(wèn)題的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • Go語(yǔ)言的隊(duì)列和堆棧實(shí)現(xiàn)方法

    Go語(yǔ)言的隊(duì)列和堆棧實(shí)現(xiàn)方法

    這篇文章主要介紹了Go語(yǔ)言的隊(duì)列和堆棧實(shí)現(xiàn)方法,涉及container/list包的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • gin項(xiàng)目部署到服務(wù)器并后臺(tái)啟動(dòng)的步驟

    gin項(xiàng)目部署到服務(wù)器并后臺(tái)啟動(dòng)的步驟

    本文主要介紹了gin項(xiàng)目部署到服務(wù)器并后臺(tái)啟動(dòng)的步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • 初學(xué)Go必備的vscode插件及最常用快捷鍵和代碼自動(dòng)補(bǔ)全

    初學(xué)Go必備的vscode插件及最常用快捷鍵和代碼自動(dòng)補(bǔ)全

    這篇文章主要給大家介紹了關(guān)于初學(xué)vscode寫(xiě)Go必備的vscode插件及最常用快捷鍵和代碼自動(dòng)補(bǔ)全的相關(guān)資料,由于vscode是開(kāi)源免費(fèi)的,而且開(kāi)發(fā)支持vscode的插件相對(duì)比較容易,更新速度也很快,需要的朋友可以參考下
    2023-07-07
  • 解決vscode中g(shù)olang插件依賴(lài)安裝失敗問(wèn)題

    解決vscode中g(shù)olang插件依賴(lài)安裝失敗問(wèn)題

    這篇文章主要介紹了解決vscode中g(shù)olang插件依賴(lài)安裝失敗問(wèn)題,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-08-08
  • Go中l(wèi)og包異或組合配置妙用詳解

    Go中l(wèi)og包異或組合配置妙用詳解

    在 Go 語(yǔ)言的 log 包中,使用“位運(yùn)算相或” (|) 來(lái)配置日志的 flag,可以讓我們靈活地組合多種日志信息輸出選項(xiàng),下面我們就來(lái)看看這種方法的好處和原理吧
    2024-11-11
  • Golang并發(fā)編程中Context包的使用與并發(fā)控制

    Golang并發(fā)編程中Context包的使用與并發(fā)控制

    Golang的context包提供了在并發(fā)編程中傳遞取消信號(hào)、超時(shí)控制和元數(shù)據(jù)的功能,本文就來(lái)介紹一下Golang并發(fā)編程中Context包的使用與并發(fā)控制,感興趣的可以了解一下
    2024-11-11

最新評(píng)論

长乐市| 凤台县| 玉屏| 枣阳市| 甘孜县| 微博| 临安市| 安阳县| 麻栗坡县| 乌拉特后旗| 那坡县| 耒阳市| 华宁县| 平塘县| 合水县| 边坝县| 吉隆县| 南澳县| 镇江市| 凤山市| 北川| 雅安市| 永丰县| 濮阳县| 北京市| 阿克陶县| 宁武县| 英吉沙县| 临武县| 化德县| 奉节县| 烟台市| 苏尼特右旗| 永靖县| 定安县| 靖江市| 云浮市| 建昌县| 漳州市| 沧源| 盐亭县|