Go 并发实战 -- sync Cond

前言

go中的sync.Cond也就是condition,是一个条件同步变量,与Java中Object的wait、notify、notifyAll方法或者Condition类的作用比较类似,如果有这方面的基础学习起来会非常简单。其实Java中的JUC包实现的可以是最丰富和易用的了,熟知JUC的话,学习其他语言的并发特性及工具的话会非常简单。

语法基础

sync.Cond同其他并发条件变量一样,提供了阻塞和唤醒函数:
Wait() 阻塞操作
Signal() 唤醒一个协程
Broadcast() 唤醒所有协程
不同的Cond需要我们制定一把锁,通常是Mutex、RWMytex,当然也可以是你自己实现的锁。
下面来看一下sync.Cond的使用:

func main() {
    lock := &sync.Mutex{}
    cond := sync.NewCond(lock)
    for i:=0; i<10; i++ {
        runGorotine(cond, i)
    }
    time.Sleep(1*time.Millisecond)
    fmt.Println("----------------------------: signal 唤醒单个")
    cond.Signal()
    time.Sleep(1*time.Millisecond)
    fmt.Println("----------------------------: broadcast 唤醒全部")
    cond.Broadcast()
    time.Sleep(2*time.Second)
}

func runGorotine(cond *sync.Cond, i int) {
    go func(cond *sync.Cond, i int) {
        cond.L.Lock()
        for condition() {
            fmt.Println("-goroutine-" + strconv.Itoa(i) + " 命中wait")
            cond.Wait()
        }
        fmt.Println("-goroutine-" + strconv.Itoa(i) + " 命中条件")
        cond.L.Unlock()
    }(cond, i)
}

func condition() bool {
    rand.Intn(50)
    if rand.Intn(50) > 20 {
        fmt.Print(true)
        return true
    }
    fmt.Print(false)
    return false
}

输出:


image.png

ps:go 协程之后启动后并不是立即执行的,需要有一定的分配过程及等待的时间,所以说sleep一小段时间,否则唤醒通知在wait之前发生就没有意义了。
上述就是Cond的最简单的使用,生产环境比这个demo要复杂一些,但是大致也就这样了。

实现原理

Cond的实现非常简单,锁操作依赖的是我们创建的lock。然后依赖于runtime阻塞和唤醒go协程的函数。

type Cond struct {
// 这个已经不是第一次见了,第一次使用后就不能copy了
    noCopy noCopy
    L Locker // 创建cond是传入的锁
    notify  notifyList // 调用runtime阻塞和唤醒的函数,内部维护了一个阻塞链表
    checker copyChecker // 保留指向自身的指针以检测对象复制。
}
// 构造函数
func NewCond(l Locker) *Cond {
    return &Cond{L: l}
}

wait函数的锁逻辑有点奇怪,其实主要是为了把外面条件判断和添加阻塞队列变为一个原子操作,这种锁的使用方式其实不太建议,比较容易出问题。

func (c *Cond) Wait() {
    c.checker.check()
    t := runtime_notifyListAdd(&c.notify) // 添加阻塞列表
    c.L.Unlock() // 函数调用前已经加锁了,在添加阻塞队列后就可以撤销锁了
    runtime_notifyListWait(&c.notify, t) // 进行阻塞
    c.L.Lock() // 外层还有个解锁操作,加把锁
}

下面是唤醒操作:

func (c *Cond) Signal() {
    c.checker.check()
    runtime_notifyListNotifyOne(&c.notify) 
    // 唤醒一个
}

func (c *Cond) Broadcast() {
    c.checker.check()
    runtime_notifyListNotifyAll(&c.notify)
    // 唤醒多个
}

关于Cond的使用及源码实现暂时介绍这么多。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容