Go sync.Cond 原理

文章目录

    • 前言
    • newCond
    • Wait
    • Signal
    • Broadcast
    • 和 channel 比较的优势

前言

  • 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

你可能感兴趣的:(Golang,#,go从入门到精通,golang,开发语言,后端)