package main
import("fmt""sync""time")// 示例来自https://stackoverflow.com/questions/36857167/how-to-correctly-use-sync-cond//fatal error: all goroutines are asleep - deadlock!//goroutine 1 [sync.Cond.Wait]:
因为c.Wait()里面加锁,而外面没有解锁,造成死锁
funcsyncCondErr(){
m := sync.Mutex{}
c := sync.NewCond(&m)gofunc(){
time.Sleep(1* time.Second)
c.Broadcast()}()
m.Lock()
time.Sleep(2* time.Second)
c.Wait()}//Tip:正确使用方式funcsyncCondExplain(){
m := sync.Mutex{}
c := sync.NewCond(&m)// Tip: 主协程先获得锁
c.L.Lock()gofunc(){// Tip: 协程一开始无法获得锁
c.L.Lock()defer c.L.Unlock()
fmt.Println("3. 该协程获得了锁")
time.Sleep(2* time.Second)// Tip: 通过notify进行广播通知,cond.Signal()用于随机一个单播
c.Broadcast()
fmt.Println("4. 该协程执行完毕,即将执行defer中的解锁操作")}()
fmt.Println("1. 主协程获得锁")
time.Sleep(1* time.Second)
fmt.Println("2. 主协程依旧抢占着锁获得锁")// Tip: 看一下Wait的大致实现,可以了解到,它是先释放锁,阻塞等待,直到收到了notify,又进行加锁
c.Wait()// Tip: 记得释放锁
c.L.Unlock()
fmt.Println("Done")}//Tip:例子funcsyncCond(){
lock := sync.Mutex{}
cond := sync.NewCond(&lock)for i :=0; i <5; i++{gofunc(i int){
cond.L.Lock()defer cond.L.Unlock()
cond.Wait()
fmt.Printf("No.%d Goroutine Receive\n", i)}(i)}
time.Sleep(time.Second)
cond.Broadcast()//cond.Signal()
time.Sleep(time.Second)}
源码分析
//Tip:wait先解锁,阻塞等待notify,得到后再加锁func(c *Cond)Wait(){
c.checker.check()
t :=runtime_notifyListAdd(&c.notify)
c.L.Unlock()runtime_notifyListWait(&c.notify, t)
c.L.Lock()}//广播func(c *Cond)Broadcast(){
c.checker.check()runtime_notifyListNotifyAll(&c.notify)}// notifyListNotifyAll notifies all entries in the list.//go:linkname notifyListNotifyAll sync.runtime_notifyListNotifyAllfuncnotifyListNotifyAll(l *notifyList){// Go through the local list and ready all waiters.for s !=nil{
next := s.next
s.next =nilreadyWithTime(s,4)
s = next
}}//单播func(c *Cond)Signal(){
c.checker.check()runtime_notifyListNotifyOne(&c.notify)}// notifyListNotifyOne notifies one entry in the list.//go:linkname notifyListNotifyOne sync.runtime_notifyListNotifyOnefuncnotifyListNotifyOne(l *notifyList){for p, s :=(*sudog)(nil), l.head; s !=nil; p, s = s, s.next {if s.ticket == t {
n := s.next
if p !=nil{
p.next = n
}else{
l.head = n
}if n ==nil{
l.tail = p
}unlock(&l.lock)
s.next =nilreadyWithTime(s,4)return}}unlock(&l.lock)}