Go sync.Cond 原理

前言

  • sync.Cond 基本很少使用,应为大部分都能使用 channel 代替
  • sync.Cond 通常是基于 sync.Mutex 扩展的
  • 主要就四个方法
    • newCond(l locker) 创建Cond
    • Wait() 阻塞等待
    • Signal() 唤醒其中一个
    • Broadcast() 唤醒全部

newCond

  • 需要传入一个 实现 locker 的锁对象,Wait 方法中会使用到

// NewCond returns a new Cond with Locker l.
func NewCond(l Locker) *Cond {
	return &Cond{L: l}
}

Wait

  • checker.check() 进行copy检查
  • runtime_notifyListAdd 加入待通知队列
  • runtime_notifyListWait 阻塞等待唤醒
func (c *Cond) Wait() {
	c.checker.check()
	t := runtime_notifyListAdd(&c.notify)
	c.L.Unlock()
	runtime_notifyListWait(&c.notify, t)
	c.L.Lock()
}

由于 Wait 开头部分调用了c.L.Unlock(),尾部调用了 c.L.Lock(),所以 wait 要在 c.L.Lock() 和 c.L.Unlock() 之间调用

package main

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

func main() {
	var wg sync.WaitGroup
	wg.Add(2)
	s := sync.NewCond(&sync.Mutex{})
	go func() {
		s.L.Lock()
		fmt.Println("开始进入等待了")
		s.Wait()
		fmt.Println("需要这样调用哈!")
		s.L.Unlock()
		wg.Done()
	}()

	go func() {
		time.Sleep(2 * time.Second)
		s.Signal()
		wg.Done()
		fmt.Println("等待我唤醒吧!")
	}()

	wg.Wait()
	fmt.Println("ending")
}

image.png

Signal

  • 从等待队列中唤醒一个
func (c *Cond) Signal() {
	c.checker.check()
	runtime_notifyListNotifyOne(&c.notify)
}

Broadcast

  • 唤醒全部
  • 由于 Wait 中结尾的 c.L.Lock() 全部唤醒还是要参与锁竞争的
func (c *Cond) Broadcast() {
	c.checker.check()
	runtime_notifyListNotifyAll(&c.notify)
}

和 channel 比较的优势

  • 单个唤醒使用 channel 替代 还是挺方便的
  • 在全部唤醒下存在优势
    • 如果想要全部唤醒,channel需要维护等待的队列
    • 由于队列长度不确定,需要使用 slice
    • slice 并发不安全,那么还需要加锁
    • runtime_notifyListNotifyAll + runtime_notifyListNotifyOne 的性能优于 channel + select + slice FIFO的实现

k8s中还有很多关于 sync.Cond 的用法,比如 k8s中的 sync.Cond

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值