golang并发编程-05-同步-02-条件变量(Cond结构体的常用方法)

@

1. sync.Cond结构体

  • 结构体定义
type Cond struct {
    noCopy  noCopy
    L       Locker
    notify  notifyList
    checker copyChecker
}
  • 作用
    实现了一个条件变量,用于等待一个或一组协程满足条件后唤醒这些协程。

从结构体定义可知,每个Cond关联一个Locker(*Mutex或*RWMutex)。

2. sync.Cond结构体常用方法

  • NewCond 创建实例
cadence := sync.NewCond(&sync.Mutex{})
  • 广播唤醒所有
go func() {
   for range time.Tick(1 * time.Millisecond) {
      cadence.Broadcast()
   }
}()
  • 唤醒一个协程
go func() {
   for range time.Tick(1 * time.Millisecond) {
      cadence.Signal()
   }
}()
  • 等待
c.L.Lock()
for !condition() {
    c.Wait()
}
... make use of condition ...
c.L.Unlock()

3. 使用示例

3.1 示例(蜀军的作战会议——广播唤醒)

package main

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

var done = false

//读函数
func read(name string, c *sync.Cond) {
    //关联锁
	c.L.Lock()
	//先让读函数等待通知
	for !done {
		fmt.Printf("【%s】 来了,等在门外\n",name)
		c.Wait()
	}
	fmt.Printf("【%s】 入账 ====》\n",name)
	c.L.Unlock()
}

//写函数
func write(name string, c *sync.Cond) {
	fmt.Printf("【%s】入帐,作战计划制定中~~~~~ \n",name)
	time.Sleep(2*time.Second)
	c.L.Lock()
	done = true
	c.L.Unlock()
	fmt.Printf("======== 作战计划制定完毕 ========\n【%s】 大家进来开会吧! \n",name)
	c.Broadcast()
}

func main() {
	cond := sync.NewCond(&sync.Mutex{})
	fmt.Println("========= 玄德公 升帐了 =============")
	for _,name := range []string{"关羽","张飞","赵云"}{
		go read(name, cond)
	}
	go write("孔明", cond)

	time.Sleep(time.Second * 3)
}
  • 输出
========= 玄德公 升帐了 =============
【孔明】入帐,作战计划制定中~~~~~ 
【关羽】 来了,等在门外
【赵云】 来了,等在门外
【张飞】 来了,等在门外
======== 作战计划制定完毕 ========
【孔明】 大家进来开会吧! 
【张飞】 入账 ====》
【赵云】 入账 ====》
【关羽】 入账 ====》

3.2 示例(蜀军薪资调整了——单独唤醒)

  • 将上例中 write()最后的广播换成单独唤醒c.Signal():
func write(name string, c *sync.Cond) {
	fmt.Printf("【%s】入帐,作战计划制定中~~~~~ \n",name)
	time.Sleep(2*time.Second)
	c.L.Lock()
	done = true
	c.L.Unlock()
	fmt.Printf("======== 作战计划制定完毕 ========\n【%s】 大家进来开会吧! \n",name)
	c.Signal()
}

结果

随机有个一等待的人入账,之后就没有然后了。因为武将们没有执行唤醒其他人。

========= 玄德公 升帐了 =============
【孔明】入帐,作战计划制定中~~~~~ 
【张飞】 来了,等在门外
【赵云】 来了,等在门外
【关羽】 来了,等在门外
======== 作战计划制定完毕 ========
【孔明】 大家进来开会吧! 
【张飞】 入账 ====》
  • 再给上例武将们的函数最后添加一个单独唤醒,所有武将将一个一个被换入

为了方便看,在read()中加了一个sleep。
另外,读锁是不互斥的,因此有几个人留在帐内我们并不关心。

func read(name string, c *sync.Cond) {
	c.L.Lock()
	for !done {
		fmt.Printf("【%s】 来了,等在门外\n",name)
		c.Wait()
	}
	fmt.Printf("【%s】 入账 ====》\n",name)
	time.Sleep(time.Second)
	c.L.Unlock()
	c.Signal()
}
  • 完整示例

最终修改一下,让刘备单独召见每一个人,大家一个完事喊下一个。

var done = false

//读函数
func read(name string, c *sync.Cond) {
	c.L.Lock()
	for !done {
		fmt.Printf("【%s】 来了,等在门外\n",name)
		c.Wait()
	}
	fmt.Printf("【%s】 入账 ====》\n",name)
	time.Sleep(time.Second)
	c.L.Unlock()
	fmt.Printf("【%s】出帐喊:下一个进来吧! \n",name)
	c.Signal()
}


func write(name string, c *sync.Cond) {
	fmt.Printf("【%s】入帐,汇报蜀将薪资调整状况 ~~~~~ \n",name)
	time.Sleep(2*time.Second)
	c.L.Lock()
	done = true
	c.L.Unlock()
	fmt.Printf("【%s】出帐喊:下一个进来吧! \n",name)
	c.Signal()
}

func main() {
	cond := sync.NewCond(&sync.Mutex{})
	fmt.Println("========= 玄德公 升帐了 =============")
	for _,name := range []string{"关羽","张飞","赵云"}{
		go read(name, cond)
	}
	go write("孔明", cond)

	time.Sleep(time.Second * 10)
}

打印结果

========= 玄德公 升帐了 =============
【孔明】入帐,汇报蜀将薪资调整状况 ~~~~~ 
【关羽】 来了,等在门外
【张飞】 来了,等在门外
【赵云】 来了,等在门外
【孔明】出帐喊:下一个进来吧! 
【关羽】 入账 ====》
【关羽】出帐喊:下一个进来吧! 
【张飞】 入账 ====》
【张飞】出帐喊:下一个进来吧! 
【赵云】 入账 ====》
【赵云】出帐喊:下一个进来吧!

posted on 2022-05-13 22:18  运维开发玄德公  阅读(14)  评论(0编辑  收藏  举报  来源

导航