中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

go并發編程sync.Cond使用場景及實現原理是什么

發布時間:2022-08-31 13:51:35 來源:億速云 閱讀:117 作者:iii 欄目:開發技術

這篇文章主要講解了“go并發編程sync.Cond使用場景及實現原理是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“go并發編程sync.Cond使用場景及實現原理是什么”吧!

使用場景

sync.Cond是go標準庫提供的一個條件變量,用于控制一組goroutine在滿足特定條件下被喚醒。

sync.Cond常用于一組goroutine等待,一個goroutine通知(事件發生)的場景。如果只有一個goroutine等待,一個goroutine通知(事件發生),使用Mutex或者Channel就可以實現。

可以用一個全局變量標志特定條件condition,每個sync.Cond都必須要關聯一個互斥鎖(Mutex或者RWMutex),當condition發生變更或者調用Wait時,都必須加鎖,保證多個goroutine安全地訪問condition。

下面是go標準庫http中關于pipe的部分實現,我們可以看到,pipe使用sync.Cond來控制管道中字節流的寫入和讀取,在pipe中數據可用并且字節流復制到pipe的緩沖區之前,所有的需要讀取該管道數據的goroutine都必須等待,直到數據準備完成。

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未完成前調用Wait進入等待
   }
}
// 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)
}

實現原理

type Cond struct {
   noCopy noCopy       // 用來保證結構體無法在編譯期間拷貝
   // L is held while observing or changing the condition
   L Locker             // 用來保證condition變更安全
   notify  notifyList   // 待通知的goutine列表
   checker copyChecker  // 用于禁止運行期間發生的拷貝
}
type notifyList struct {
   wait   uint32      // 正在等待的goroutine的ticket
   notify uint32      // 已經通知到的goroutine的ticket
   lock   uintptr // key field of the mutex
   head   unsafe.Pointer     // 鏈表頭部
   tail   unsafe.Pointer     // 鏈表尾部
}

copyChecker

copyChecker是一個指針類型,在創建時,它的值指向自身地址,用于檢測該對象是否發生了拷貝。如果發生了拷貝,則直接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

調用 Wait 會自動釋放鎖 c.L,并掛起調用者所在的 goroutine,因此當前協程會阻塞在 Wait 方法調用的地方。如果其他協程調用了 Signal 或 Broadcast 喚醒了該協程,那么 Wait 方法在結束阻塞時,會重新給 c.L 加鎖,并且繼續執行 Wait 后面的代碼。

對條件的檢查,使用了 for !condition() 而非 if,是因為當前協程被喚醒時,條件不一定符合要求,需要再次 Wait 等待下次被喚醒。為了保險起見,使用 for 能夠確保條件符合要求后,再執行后續的代碼。

func (c *Cond) Wait() {
   c.checker.check()
   t := runtime_notifyListAdd(&c.notify)
   c.L.Unlock()
   runtime_notifyListWait(&c.notify, t)
   c.L.Lock()
}
  • 檢查Cond是否被復制,如果被復制,直接panic;

  • 調用runtime_notifyListAdd調用者添加到通知列表并解鎖,以便可以接收到通知,然后將返回的ticket傳入到runtime_notifyListWait來等待通知。

  • 當前goroutine會阻塞在wait調用的地方,直到其他goroutine調用Signal或Broadcast喚醒該協程。

func notifyListAdd(l *notifyList) uint32 {
    return atomic.Xadd(&l.wait, 1) - 1
}

notifyListWait會將當前goroutine追加到鏈表的尾端,同時調用goparkunlock讓當前goroutine陷入休眠,該方法會直接讓出當前處理器的使用權并等待調度器的喚醒。

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會喚醒隊列最前面的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會喚醒隊列中全部的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并發編程sync.Cond使用場景及實現原理是什么”的內容了,經過本文的學習后,相信大家對go并發編程sync.Cond使用場景及實現原理是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

沂南县| 新和县| 永善县| 那坡县| 榆社县| 濮阳县| 临沂市| 玉山县| 明光市| 嵊州市| 望都县| 霍城县| 甘洛县| 溧水县| 吉木乃县| 屏东县| 绥阳县| 旌德县| 延川县| 连平县| 潞城市| 全南县| 南华县| 平泉县| 枝江市| 新邵县| 丁青县| 天峻县| 南汇区| 土默特右旗| 准格尔旗| 普格县| 石阡县| 新丰县| 夏邑县| 永修县| 饶河县| 通渭县| 张家界市| 汪清县| 东方市|