## 标签
```routine```、```sync.Mutex```、 ```sync.Cond```
## 场景
利用```sync.Cond```的Broadcast()方法唤醒3个routine
## 过程
**代码**
```golang
package main
import (
"fmt"
"sync"
"time"
)
func main() {
wg := sync.WaitGroup{}
mux := sync.Mutex{}
cond := sync.NewCond(&mux)
wg.Add(3)
for i := 0; i < 2; i++ {
go func(num int) {
defer wg.Done()
fmt.Println("routine", num, "开始处理")
mux.Lock()
fmt.Println(" routine", num, "锁住了mux @", time.Now())
cond.Wait()
fmt.Println(" routine", num, "马上解锁mux @", time.Now())
mux.Unlock()
}(i)
}
go func() {
defer wg.Done()
time.Sleep(time.Second * 10)
fmt.Println("预备,开始!")
cond.Broadcast()
}()
wg.Wait()
}
```
> 逻辑解释
>
> 1、启动2个routine,在每个routine中:
> * 1) 申请锁住mux
>
> * 2) 等待cond的广播信号
>
> * 3) 解锁mux
>
> 2、再启动一个routine,等待10秒后触发cond.Broadcast(),唤醒1.2)之后的操作
**执行结果**
```text
routine 0 开始处理
routine 1 开始处理
routine 0 锁住了mux @ 2020-10-28 18:49:23.1134126 +0800 CST m=+0.003989001
routine 1 锁住了mux @ 2020-10-28 18:49:23.1333588 +0800 CST m=+0.023935201
预备,开始!
routine 1 马上解锁mux @ 2020-10-28 18:49:33.1143597 +0800 CST m=+10.004936101
routine 0 马上解锁mux @ 2020-10-28 18:49:33.1143597 +0800 CST m=+10.004936101
Process finished with exit code 0
```
## 结果分析
从结果看,两个等待的routine在彼此释放锁mux之前都分别获取到了mux的锁,这是比较怪异的地方,让我们来看一下sync.Cond.Wait()方法的逻辑:
```golang
func (c *Cond) Wait() {
c.checker.check()
t := runtime_notifyListAdd(&c.notify)
c.L.Unlock() // 原来幺蛾子出在这儿,在routine执行mux.Unlock()方法之前,sync.Cond先执行了,这就导致另一个routine逮到了机会,锁住了mux
runtime_notifyListWait(&c.notify, t)
c.L.Lock()
}
```
那么问题来了,针对这个场景,应该怎样使用sync.Cond与sync.Mutex配合routine实现最初的目的呢,求大佬科普 ???
看了郝林老师的解释,理解了,原文:
> 因为条件变量的Wait方法在阻塞当前的 goroutine 之前,会解锁它基于的互斥锁,所以在调用该Wait方法之前,我们必须先锁定那个互斥锁,否则在调用这个Wait方法时,就会引发一个不可恢复的 panic。
>
> 为什么条件变量的Wait方法要这么做呢?你可以想象一下,如果Wait方法在互斥锁已经锁定的情况下,阻塞了当前的 goroutine,那么又由谁来解锁呢?别的 goroutine 吗?
>
> 先不说这违背了互斥锁的重要使用原则,即:成对的锁定和解锁,就算别的 goroutine 可以来解锁,那万一解锁重复了怎么办?由此引发的 panic 可是无法恢复的。
>
> 如果当前的 goroutine 无法解锁,别的 goroutine 也都不来解锁,那么又由谁来进入临界区,并改变共享资源的状态呢?只要共享资源的状态不变,即使当前的 goroutine 因收到通知而被唤醒,也依然会再次执行这个Wait方法,并再次被阻塞。
>
> 所以说,如果条件变量的Wait方法不先解锁互斥锁的话,那么就只会造成两种后果:不是当前的程序因 panic 而崩溃,就是相关的 goroutine 全面阻塞。
#1