sync.Cond学习笔记

byte · · 680 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

sync.Cond

go/sync

sync.Cond用于goroutine之间的协作,用于协程的挂起和唤醒。当多个协程之间协作时,有可能一个协程需要依赖别的协程完成后再进行某种操作,这时可依据管道进行通信,这对于两个协程之间是比较方便的。但若是多个协程依赖一个协程的完成后再进行某种操作,那么就可以使用sync.Cond来实现了;

例子:

package main

import (
    "fmt"
    "sync"
    "time"
)

var locker = new(sync.Mutex)
var cond = sync.NewCond(locker)

func main() {
    for i := 0; i < 40; i++ {
        go func(x int) {
            cond.L.Lock()         //获取锁
            cond.Wait()           //等待通知,阻塞当前goroutine
              cond.L.Unlock()       //释放锁
            fmt.Println(x)
            time.Sleep(time.Second * 1)

        }(i)
    }
    time.Sleep(time.Second * 1)
    fmt.Println("Signal...")
    cond.Signal() // 下发一个通知给已经获取锁的goroutine
    time.Sleep(time.Second * 1)
    cond.Signal() // 3秒之后 下发一个通知给已经获取锁的goroutine
    time.Sleep(time.Second * 3)
    cond.Broadcast() //3秒之后 下发广播给所有等待的goroutine
    fmt.Println("Broadcast...")
    time.Sleep(time.Second * 60)
}

协程在使用cond时,首先需要对cond持有的锁进行加锁操作,然后调用Wait函数等待,后续操作会阻塞在Wait处;当主协程决定唤醒等待的协程时,它会使用Signal(随机唤醒一个等待的协程)或Broadcast(唤醒所有等待的协程)唤醒等待的协程。当某一协程被唤醒后,Wait函数会返回,此时需要解锁,之后便可以做后续的操作了;

实现细节:
cond的数据结构

type Cond struct {
    noCopy noCopy

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

    notify  notifyList
    checker copyChecker
}

Cond结构体持有一把锁,这个锁用来做协程间的同步操作;notify是一个唤醒队列,所有调用过Wait方法的协程都会被加入到这个唤醒队列中

Wait函数

func (c *Cond) Wait() {
    c.checker.check()
    t := runtime_notifyListAdd(&c.notify)
    c.L.Unlock()
    runtime_notifyListWait(&c.notify, t)
    c.L.Lock()
}

首先,当前协程会被加入到notifyList中,之后会解锁L,这是因为以便让别的协程可以获取到锁,做同样的操作(lock, wait, unlock)中的lock这一步;之后runtime_notifyListWait会阻塞,即所有调用过Wait函数的协程都会阻塞在这个地方;
之后若该协程被唤醒,那么runtime_notifyListWait会返回,此时又回重新给L加锁,然后第一个抢占到这个返回操作的协程,它的Wait操作真正返回了;这个协程的Wait函数外部,可能后续还会做一些需要互斥的操作,在这个协程做完所有操作后,按照规定它会调用Unlock解锁,这时别的协程的Wait函数才能真正的返回,做后续操作;

关于唤醒函数就比较简单了:

func (c *Cond) Signal() {
    c.checker.check()
    runtime_notifyListNotifyOne(&c.notify)
}

func (c *Cond) Broadcast() {
    c.checker.check()
    runtime_notifyListNotifyAll(&c.notify)
}

从队列中唤醒即可


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:byte

查看原文:sync.Cond学习笔记

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

680 次点击  ∙  2 赞  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传