1. sync包的Cond,提供条件变数。
a. 条件变数是基于互斥锁的,它必须有互斥锁的支撑,才能使用。
b. 条件变数并不是被用来保护共享资源,它是用来协调想要访问共享资源的那些goroutines。
当共享资源的状态发生改变时,它可以被用来通知被互斥锁阻塞的goroutine。
c. 函数:
func NewCond(l Locker) *Condfunc (c *Cond) Broadcast()func (c *Cond) Signal()func (c *Cond) Wait()func (rw *RWMutex) Lock()func (rw *RWMutex) Unlock()func (rw *RWMutex) RLock()func (rw *RWMutex) RUnlock()
2. 条件变数如何和读/写互斥锁一起使用?
参考範例:
package mainimport ("log""sync""time")func main() {// mailbox 代表信箱。// 0代表信箱是空的,1代表信箱是满的。var mailbox uint8// lock 代表信箱上的锁。var lock sync.RWMutex// sendCond 代表专用于发信的条件变数。sendCond := sync.NewCond(&lock)// recvCond 代表专用于收信的条件变数。recvCond := sync.NewCond(lock.RLocker())// sign 用于传递演示完成的信号。sign := make(chan struct{}, 3)max := 5go func(max int) { // 用于发信。defer func() {sign <- struct{}{}}()for i := 1; i <= max; i++ {time.Sleep(time.Millisecond * 500)lock.Lock()for mailbox == 1 {sendCond.Wait()}log.Printf("sender [%d]: the mailbox is empty.", i)mailbox = 1log.Printf("sender [%d]: the letter has been sent.", i)lock.Unlock()recvCond.Signal()}}(max)go func(max int) { // 用于收信。defer func() {sign <- struct{}{}}()for j := 1; j <= max; j++ {time.Sleep(time.Millisecond * 500)lock.RLock()for mailbox == 0 {recvCond.Wait()}log.Printf("receiver [%d]: the mailbox is full.", j)mailbox = 0log.Printf("receiver [%d]: the letter has been received.", j)lock.RUnlock()sendCond.Signal()}}(max)<-sign<-sign}
https://play.golang.org/p/qY4mLfzKSuG
3. 範例细节说明
a. 与sync.Mutex和sync.RWMutex类型不同,sync.Cond类型并不是开箱即用。
需要利用sync.NewCond函数创建它的指标值,而这个函数需要sync.Locker类型的参数值。
b. sendCond和recvCond都是属于*sync.Cond类型,同时使用sync.NewCond函数初始化。
sendCond := sync.NewCond(&lock)recvCond := sync.NewCond(lock.RLocker())
sendCond变数在初始化时,要把lock的指标值传给sync.NewCond函数。
因为,sendCond专门用来对共享资源的写操作。lock变数的Lock和Unlock方法分别用于对写锁的锁定和解锁。
recvCond变数在初始化时,只要把RLocker的值传给sync.NewCond函数。
因为,recvCond只会对共享资源进行读操作。
c. 使用条件变数(sendCond、recvCond),实现单向通知(sendCond.Signal()、recvCond.Signal())。
sender的goroutine开始执行,会阻塞在sendCond.Wait()等待有人发送Signal(sendCond.Signal())。
当sender收到Signal后,就会继续往下执行。
而receiver也是。
参考来源:
郝林-Go语言核心36讲
https://github.com/hyper0x/Golang_Puzzlers
https://golang.org/pkg/cmd/go/internal/test/