iT邦幫忙

0

[Golang]同步工具-sync包的Cond-心智圖總結

1. sync包的Cond,提供條件變數。
a. 條件變數是基於互斥鎖的,它必須有互斥鎖的支撐,才能使用。
b. 條件變數並不是被用來保護共享資源,它是用來協調想要訪問共享資源的那些goroutines。
當共享資源的狀態發生改變時,它可以被用來通知被互斥鎖阻塞的goroutine。
c. 函數:

func NewCond(l Locker) *Cond
func (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 main

import (
	"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 := 5
	go 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 = 1
			log.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 = 0
			log.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也是。

https://ithelp.ithome.com.tw/upload/images/20201120/20131728gfW5BXgqnB.png

參考來源:
郝林-Go语言核心36讲
https://github.com/hyper0x/Golang_Puzzlers
https://golang.org/pkg/cmd/go/internal/test/


尚未有邦友留言

立即登入留言