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)
}
打印结果
========= 玄德公 升帐了 =============
【孔明】入帐,汇报蜀将薪资调整状况 ~~~~~
【关羽】 来了,等在门外
【张飞】 来了,等在门外
【赵云】 来了,等在门外
【孔明】出帐喊:下一个进来吧!
【关羽】 入账 ====》
【关羽】出帐喊:下一个进来吧!
【张飞】 入账 ====》
【张飞】出帐喊:下一个进来吧!
【赵云】 入账 ====》
【赵云】出帐喊:下一个进来吧!