前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go 并发实战 -- sync Cond

Go 并发实战 -- sync Cond

作者头像
邹志全
发布2019-07-31 10:58:26
8570
发布2019-07-31 10:58:26
举报
文章被收录于专栏:EffectiveCodingEffectiveCoding
前言

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

语法基础

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

代码语言:javascript
复制
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协程的函数。

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

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

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

下面是唤醒操作:

代码语言:javascript
复制
func (c *Cond) Signal() {
    c.checker.check()
    runtime_notifyListNotifyOne(&c.notify) 
    // 唤醒一个
}

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

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.07.10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 语法基础
  • 实现原理
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档