golang并发编程-05-同步-02-条件变量(Cond结构体的常用方法)
文章目录
- 1. sync.Cond结构体
- 2. sync.Cond结构体常用方法
- 3. 使用示例
-
- 3.1 示例(蜀军的作战会议——广播唤醒)
- 3.2 示例(蜀军薪资调整了——单独唤醒)
1. sync.Cond结构体
- 结构体定义
type Cond struct { noCopy noCopy LLocker 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 mainimport ("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 = truec.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 = truec.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 = truec.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)}
打印结果
========= 玄德公 升帐了 =============【孔明】入帐,汇报蜀将薪资调整状况 ~~~~~ 【关羽】 来了,等在门外【张飞】 来了,等在门外【赵云】 来了,等在门外【孔明】出帐喊:下一个进来吧! 【关羽】 入账 ====》【关羽】出帐喊:下一个进来吧! 【张飞】 入账 ====》【张飞】出帐喊:下一个进来吧! 【赵云】 入账 ====》【赵云】出帐喊:下一个进来吧!