TL;DR

スレッド間の排他制御(ミューテックス)と条件待ちを、カプセル化したデザインパターン。

仕組み

Monitor pattern には次の特徴がある。

  • ミューテックスを内包し、データの排他制御を保証する
  • 条件変数を使って、スレッドを待機させたり通知したりする
  • 同期処理をカプセル化し、外部のスレッドがミューテックスを直接扱わないようにする

例

Go では、sync.Cond が持つミューテックス(sync.Locker)を使うたびに Monitor pattern を適用することになる。次の例では、stingy がお金を足して Signal() で通知し、spendy は残高が 50 未満の間 Wait() で待機する。

func stingy(money *int, cond *sync.Cond) {
	for i := 0; i < 1_000_000; i++ {
		cond.L.Lock()
		*money += 10
		cond.Signal()
		cond.L.Unlock()
	}
	fmt.Println("Stingy Done")
}
 
func spendy(money *int, cond *sync.Cond) {
	for i := 0; i < 200_000; i++ {
		cond.L.Lock()
		// `*money` が 50 未満の間は `cond.Wait()` で待機
		for *money < 50 {
			cond.Wait()
		}
		*money -= 50
		if *money < 0 {
			fmt.Println("Money is negative!")
			os.Exit(1)
		}
		cond.L.Unlock()
	}
	fmt.Println("Spendy Done")
}
 
func main() {
	money := 100
	mutex := &sync.Mutex{}
	cond := sync.NewCond(mutex)
	go stingy(&money, cond)
	go spendy(&money, cond)
	time.Sleep(2 * time.Second)
	mutex.Lock()
	fmt.Println("Money in bank account:", money)
	mutex.Unlock()
}

関連