[笔记] Go sync.Cond
2020/10/22
Concept
Cond implements a condition variable, a rendezvous point for goroutines waiting for or announcing the occurrence of an event.
Each Cond has an associated Locker L (often a Mutex or RWMutex), which must be held when changing the condition and when calling the Wait method.
A Cond must not be copied after first use。
条件变量并不是被用来保护临界区和共享资源的,它是用于协调想要访问共享资源的那些线程的。当共享资源的状态发生变化时,它可以被用来通知被互斥锁阻塞的线程。
Methods
func NewCond
1func NewCond(l Locker) *Cond
NewCond returns a new Cond with Locker l.
func (*Cond) Broadcast
1func (c *Cond) Broadcast()
Broadcast wakes all goroutines waiting on c.
It is allowed but not required for the caller to hold c.L during the call.
func (*Cond) Signal
1func (c *Cond) Signal()
Signal wakes one goroutine waiting on c, if there is any.
It is allowed but not required for the caller to hold c.L during the call.
func (*Cond) Wait
1func (c *Cond) Wait()
Wait atomically unlocks c.L and suspends execution of the calling goroutine. After later resuming execution, Wait locks c.L before returning. Unlike in other systems, Wait cannot return unless awoken by Broadcast or Signal.
Because c.L is not locked when Wait first resumes, the caller typically cannot assume that the condition is true when Wait returns. Instead, the caller should Wait in a loop:
1c.L.Lock()
2for !condition() {
3 c.Wait()
4}
5... make use of condition ...
6c.L.Unlock()
Example
1package main
2
3import (
4 "log"
5 "sync"
6 "time"
7)
8
9func main() {
10 var mailbox uint8
11
12 var lock sync.RWMutex
13
14 sendCond := sync.NewCond(&lock)
15 recvCond := sync.NewCond(lock.RLocker())
16
17 var wg sync.WaitGroup
18
19 wg.Add(2)
20 max := 5
21 go func(max int) {
22 defer wg.Done()
23
24 for i:=1;i<=max;i++{
25 time.Sleep(500*time.Millisecond)
26 lock.Lock()
27 for mailbox ==1 {
28 sendCond.Wait()
29 }
30 log.Printf("sender [%d]: the mailbox is empty.",i)
31 mailbox =1
32 log.Printf("sender [%d]: the letter has been sent.", i)
33 lock.Unlock()
34 recvCond.Signal()
35 }
36 }(max)
37
38 go func(max int) {
39 defer wg.Done()
40
41 for j:=1;j<=max;j++{
42 time.Sleep(500*time.Millisecond)
43 lock.RLock()
44 for mailbox == 0{
45 recvCond.Wait()
46 }
47 log.Printf("receiver [%d]: the mailbox is full.", j)
48 mailbox = 0
49 log.Printf("receiver [%d]: the letter has been received.", j)
50 lock.RUnlock()
51 sendCond.Signal()
52 }
53 }(max)
54
55 wg.Wait()
56}
Explanation
条件变量的 Wait 方法主要做了四件事。
- 把调用它的 goroutine (也就是当前的 goroutine) 加入到当前条件变量的通知队列中。
- 解锁当前的条件变量基于的那个互斥锁。
- 让当前的 goroutine 处于等待状态,等到通知到来时再决定是否唤醒它。此时,这个 goroutine 就会阻塞在调用这个 Wait 方法的那行代码上。
- 如果通知到来并且决定唤醒这个 goroutine,那么就在唤醒它之后重新锁定当前条件变量基于的互斥锁。自此之后,当前的 goroutine 就会继续执行后面的代码了。