go Sync.Cond介紹

是什么

sync.Cond 條件變量用來(lái)協(xié)調(diào)想要訪問(wèn)共享資源的那些 goroutine巍膘,當(dāng)共享資源的狀態(tài)發(fā)生變化的時(shí)候哺徊,它可以用來(lái)通知被互斥鎖阻塞的 goroutine构资。

sync.Cond 基于互斥鎖/讀寫(xiě)鎖芜壁,經(jīng)常用在多個(gè) goroutine 等待功茴,一個(gè) goroutine 通知(事件發(fā)生)的場(chǎng)景庐冯。

怎么用

sync.Cond 的結(jié)構(gòu)

// Each Cond has an associated Locker L (often a *Mutex or *RWMutex),
// which must be held when changing the condition and
// when calling the Wait method.
//
// A Cond must not be copied after first use.
type Cond struct {
        noCopy noCopy

        // L is held while observing or changing the condition
        L Locker

        notify  notifyList
        checker copyChecker
}

sync.Cond 的四個(gè)方法

// NewCond 創(chuàng)建 Cond 實(shí)例時(shí),需要關(guān)聯(lián)一個(gè)鎖坎穿。
func NewCond(l Locker) *Cond{}

// Broadcast 喚醒所有等待條件變量 c 的 goroutine展父,無(wú)需鎖保護(hù)。
// Broadcast wakes all goroutines waiting on c.
//
// It is allowed but not required for the caller to hold c.L
// during the call.
func (c *Cond) Broadcast(){}

// Signal 喚醒一個(gè)協(xié)程
// Signal wakes one goroutine waiting on c, if there is any.
//
// It is allowed but not required for the caller to hold c.L
// during the call.
func (c *Cond) Signal(){}

// Wait 等待
// Wait atomically unlocks c.L and suspends execution
// of the calling goroutine. After later resuming execution,
// Wait locks c.L before returning. Unlike in other systems,
// Wait cannot return unless awoken by Broadcast or Signal.
//
// Because c.L is not locked when Wait first resumes, the caller
// typically cannot assume that the condition is true when
// Wait returns. Instead, the caller should Wait in a loop:
//
//    c.L.Lock()
//    for !condition() {
//        c.Wait()
//    }
//    ... make use of condition ...
//    c.L.Unlock()
//
func (c *Cond) Wait(){}

使用demo

package main

import (
    "log"
    "sync"
    "time"
)

var done = false

func read(name string, c *sync.Cond) {
    c.L.Lock()
    for !done {
        c.Wait()
    }
    log.Println(name, "starts reading")
    c.L.Unlock()
}

func write(name string, c *sync.Cond) {
    log.Println(name, "starts writing")
    time.Sleep(time.Second)
    c.L.Lock()
    done = true
    c.L.Unlock()
    log.Println(name, "wakes")
    c.Broadcast()
    //c.Signal()
}

func main() {
    cond := sync.NewCond(&sync.Mutex{})

    go read("reader1", cond)
    go read("reader2", cond)
    go read("reader3", cond)
    write("writer", cond)

    time.Sleep(time.Second * 3)
}

應(yīng)用場(chǎng)景

經(jīng)常用在多個(gè) goroutine 等待玲昧,一個(gè) goroutine 通知(事件發(fā)生)的場(chǎng)景栖茉。

比如:有一個(gè)協(xié)程在異步地接收數(shù)據(jù),剩下的多個(gè)協(xié)程必須等待這個(gè)協(xié)程接收完數(shù)據(jù)孵延,才能讀取到正確的數(shù)據(jù)

實(shí)際使用

https://github.com/panjf2000/ants.
第三方包goroutine池吕漂,ants包中pool.go文件

// retrieveWorker returns an available worker to run the tasks.
func (p *Pool) retrieveWorker() (w *goWorker) {
    spawnWorker := func() {
        w = p.workerCache.Get().(*goWorker)
        w.run()
    }

    p.lock.Lock()

    w = p.workers.detach()
    if w != nil { // first try to fetch the worker from the queue
        p.lock.Unlock()
    } else if capacity := p.Cap(); capacity == -1 || capacity > p.Running() {
        // if the worker queue is empty and we don't run out of the pool capacity,
        // then just spawn a new worker goroutine.
        p.lock.Unlock()
        spawnWorker()
    } else { // otherwise, we'll have to keep them blocked and wait for at least one worker to be put back into pool.
        if p.options.Nonblocking {
            p.lock.Unlock()
            return
        }
    retry:
        if p.options.MaxBlockingTasks != 0 && p.blockingNum >= p.options.MaxBlockingTasks {
            p.lock.Unlock()
            return
        }
        p.blockingNum++
        p.cond.Wait() // block and wait for an available worker
        p.blockingNum--
        var nw int
        if nw = p.Running(); nw == 0 { // awakened by the scavenger
            p.lock.Unlock()
            if !p.IsClosed() {
                spawnWorker()
            }
            return
        }
        if w = p.workers.detach(); w == nil {
            if nw < capacity {
                p.lock.Unlock()
                spawnWorker()
                return
            }
            goto retry
        }

        p.lock.Unlock()
    }
    return
}

// revertWorker puts a worker back into free pool, recycling the goroutines.
func (p *Pool) revertWorker(worker *goWorker) bool {
    if capacity := p.Cap(); (capacity > 0 && p.Running() > capacity) || p.IsClosed() {
        p.cond.Broadcast()
        return false
    }
    worker.recycleTime = time.Now()
    p.lock.Lock()

    // To avoid memory leaks, add a double check in the lock scope.
    // Issue: https://github.com/panjf2000/ants/issues/113
    if p.IsClosed() {
        p.lock.Unlock()
        return false
    }

    err := p.workers.insert(worker)
    if err != nil {
        p.lock.Unlock()
        return false
    }

    // Notify the invoker stuck in 'retrieveWorker()' of there is an available worker in the worker queue.
    p.cond.Signal()
    p.lock.Unlock()
    return true
}

參考

1、sync.Cond 條件變量
2尘应、一文讀懂 Go sync.Cond 設(shè)計(jì)
3惶凝、源碼剖析sync.Cond(條件變量的實(shí)現(xiàn)機(jī)制)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市犬钢,隨后出現(xiàn)的幾起案子梨睁,更是在濱河造成了極大的恐慌,老刑警劉巖娜饵,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坡贺,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)遍坟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)拳亿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人愿伴,你說(shuō)我怎么就攤上這事肺魁。” “怎么了隔节?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵鹅经,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我怎诫,道長(zhǎng)瘾晃,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任幻妓,我火速辦了婚禮蹦误,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肉津。我一直安慰自己强胰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布妹沙。 她就那樣靜靜地躺著偶洋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪距糖。 梳的紋絲不亂的頭發(fā)上玄窝,一...
    開(kāi)封第一講書(shū)人閱讀 51,698評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音肾筐,去河邊找鬼。 笑死缸剪,一個(gè)胖子當(dāng)著我的面吹牛吗铐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播杏节,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼唬渗,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了奋渔?” 一聲冷哼從身側(cè)響起镊逝,我...
    開(kāi)封第一講書(shū)人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嫉鲸,沒(méi)想到半個(gè)月后撑蒜,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年座菠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狸眼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浴滴,死狀恐怖拓萌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情升略,我是刑警寧澤微王,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站品嚣,受9級(jí)特大地震影響炕倘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腰根,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一激才、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧额嘿,春花似錦瘸恼、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至球拦,卻和暖如春靠闭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坎炼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工愧膀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谣光。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓檩淋,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親萄金。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蟀悦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容