欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語(yǔ)言sync.Cond使用方法詳解

 更新時(shí)間:2023年07月18日 11:15:48   作者:碼一行  
Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)中還包含條件變量 sync.Cond,它可以讓一組 Goroutine 都在滿(mǎn)足特定條件時(shí)被喚醒,每一個(gè)sync.Cond結(jié)構(gòu)體在初始化時(shí)都需要傳入一個(gè)互斥鎖,接下來(lái)我們將通過(guò)文中例子了解它的使用方法,感興趣的同學(xué)跟著小編一起來(lái)看看吧

概述

每一個(gè)sync.Cond結(jié)構(gòu)體在初始化時(shí)都需要傳入一個(gè)互斥鎖,我們可以通過(guò)下面的例子了解它的使用方法:

var status int64
func main(){
    c := sync.NewCond(&sync.mutex{})
    for i := 0; i < 10; i++ {
        go listen(c)
    }
    time.Sleep(1 * time.Second)
    go broadcast(c)
    ch := make(chan os.Signal, 1)
    signal.Notify(ch, os.Interrupt)
    <-ch
}
func broadcast(c *sync.Cond) {
    c.L.Lock()
    atomic.StoreInt64(&status, 1)
    c.Broadcast()
    c.L.Unlock()
}
func listen(c *sync.Cond) {
    c.L.Lock()
    for atomic.LoadInt64(&status) != 1 {
        c.Wait()
    }
    fmt.Println("listen")
    c.L.Unlock()
}

運(yùn)行結(jié)果:

listen
...
listen

上述代碼同時(shí)運(yùn)行了 11 個(gè)Goroutine,它們分別做了不同事情:

  • 10個(gè)Goroutine通過(guò)sync.Cond.Wait等待特定條件滿(mǎn)足
  • 1個(gè)Goroutine會(huì)調(diào)用sync.Cond.Broadcast喚醒所有陷入等待的Goroutine

調(diào)用sync.Cond.Broadcast方法后,上述代碼會(huì)打印出10"listen" 并結(jié)束調(diào)用。

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

sync.Cond的結(jié)構(gòu)體中包含以下 4 個(gè)字段:

type Cond struct {
    noCopy   noCopy
    L        Locker
    notify   notifyList
    checker  copyChecker
}
  • noCopy —— 用于保證結(jié)構(gòu)體不會(huì)在編譯期間復(fù)制
  • L —— 用于保護(hù)內(nèi)部的 notify 字段,Locker 接口類(lèi)型的變量
  • notify —— 一個(gè) Goroutine 的鏈表,它是實(shí)現(xiàn)同步機(jī)制的核心結(jié)構(gòu)
  • copyChecker —— 用于禁止運(yùn)行期間發(fā)生的復(fù)制
type notifyList struct {
    wait   uint32
    notify uint32
    lock   mutex
    head   *sudog
    tail   *sudog
}

sync.notifyList結(jié)構(gòu)體中,headtail分別指向鏈表的頭和尾,waitnotify分別表示當(dāng)前正在等待的和已經(jīng)通知的Goroutine的索引。

接口

sync.Cond對(duì)外暴露的sync.Cond.Wait方法會(huì)令當(dāng)前Goroutine陷入休眠狀態(tài),它的執(zhí)行過(guò)程分成以下兩個(gè)步驟:

  • 調(diào)用runtime.notifyListAdd將等待計(jì)時(shí)器加一并解鎖
  • 調(diào)用runtime.notifyListWait等待其他Goroutine被喚醒并對(duì)其加鎖
func (c *Cond) Wait () {
    c.checker.check()
    t := runtime_notifyListAdd(&c.notify)  // runtime.notifyListAdd 的鏈接名
    c.L.Unlock()
    runtime_notifyListWait(&c.notify, t)   //runtime.notifyListWait 的鏈接名
    c.L.Lock()
}
func notifyListAdd(l *notifyList) uint32 {
    return atomic.Xadd(&l.wait, 1) - 1
}

runtime.notifyListWait 會(huì)獲取當(dāng)前Goroutine并將它追加到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)
}

除了將當(dāng)前Goroutine追加到鏈表末端外,我們還會(huì)調(diào)用runtime.goparkunlock令當(dāng)前Goroutine陷入休眠。該函數(shù)也是在Go語(yǔ)言切換Goroutine時(shí)常用的方法,它會(huì)直接讓出當(dāng)前處理器的使用權(quán)并等待調(diào)度器喚醒。

sync.Cond.Signalsync.Cond.Broadcast方法就是用來(lái)喚醒陷入休眠的Goroutine的,它們的實(shí)現(xiàn)有一些細(xì)微差別:

  • sync.Cond.Signal方法會(huì)喚醒隊(duì)列最前面的Goroutine
  • sync.Cond.Broadcast方法會(huì)喚醒隊(duì)列中全部Goroutine
func (c *Cond) Signal() {
    c.checker.check()
    runtime_notifyListNotifyOne(&c.notify)
}
func (c *Cond) Broadcast() {
    c.checker.check()
    runtime_notifyListNotifyAll(&c.notify)
}

runtime.notifyListNotifyOne只會(huì)從sync.notifyList鏈表中找到滿(mǎn)足sudog.ticket == l.notify條件的Goroutine,并通過(guò)runtime.readyWithTime將其喚醒:

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.tiket == 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
        }
    }
}

runtime.notifyListNotifyAll會(huì)依次通過(guò)runtime.readyWithTime喚醒鏈表中的Goroutine

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
    }
}

Goroutine的喚醒順序也是按照加入隊(duì)列的先后順序,先加入的會(huì)先被喚醒,而后加入的Goroutine可能需要等待調(diào)度器的調(diào)度。

一般情況下,我們會(huì)先調(diào)用sync.Cond.Wait陷入休眠等待滿(mǎn)足期望條件,當(dāng)滿(mǎn)足期望條件時(shí),就可以選用sync.Cond.Signal或者sync.Cond.Broadcast喚醒一個(gè)或者全部Goroutine。

小結(jié)

sync.Cond不是常用的同步機(jī)制,但是在條件長(zhǎng)時(shí)間無(wú)法滿(mǎn)足時(shí),與使用for {}進(jìn)行忙碌等待相比,sync.Cond能夠讓出處理器的使用權(quán),提高CPU的利用率。

使用時(shí)需要注意以下問(wèn)題:

  • sync.Cond.Wait在調(diào)用之前一定要先獲取互斥鎖,否則會(huì)觸發(fā)程序崩潰
  • sync.Cond.Signal喚醒的Goroutine都是隊(duì)列最前面、等待最久的Goroutine
  • sync.Cond.Broadcast會(huì)按照一定順序廣播通知等待的全部Goroutine

到此這篇關(guān)于Go語(yǔ)言sync.Cond使用方法詳解的文章就介紹到這了,更多相關(guān)Go語(yǔ)言sync.Cond內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論