ミューテックスは最も単純なタイプのロックですが、比較的暴力的でもあり、ゴルーチンがミューテックスを取得すると、他のゴルーチンはそのゴルーチンがミューテックスを解放するまで素直に待つことしかできません。
RWMutex は比較的使いやすく、古典的な単一書き込み複数読み取りモデルです。読み取りロックの場合、書き込みはブロックされますが、読み取りはブロックされません。つまり、複数のゴルーチンが読み取りロック (RLock() メソッドの呼び出し) を同時に取得できますが、書き込みロック (Lock() メソッドの呼び出し) はブロックされます。他のゴルーチン (読み取りと書き込みに関係なく) では、ロック全体はそのゴルーチンによって排他的に所有されているのと同等です。RWMutex の実装から、RWMutex 型は実際に Mutex を結合します。
type RWMutex struct {
w Mutex
writerSem uint32
readerSem uint32
readerCount int32
readerWait int32
}
これら 2 つのロック タイプの場合、Lock() または RLock() には、対応する Unlock() または RUnlock() 呼び出しがあることが保証される必要があります。そうしないと、ロックを待機しているすべてのゴルーチンが飢餓状態になる可能性があり、死をも引き起こすロック。ロックの一般的な使用パターンは次のとおりです。
package main
import(
"fmt"
"sync"
)
var (
//論理で使われる変数
count int
//変数に対応する使用ミューテックス
countGuard sync.Mutex
)
func GetCount() int {
//ロックする
countGuard.Lock()
//関数が終了するときにロックを解除する
defer countGuard.Unlock()
リターンカウント
}
func SetCount(c int) {
countGuard.Lock()
count = c
countGuard.Unlock()
}
func main() {
//並行安全に設定できる
SetCount(1)
//並行安全に取得できる
fmt.Println(GetCount())
}
コードの説明は次のとおりです。
- 10 行目は論理ステップで使用される変数です。パッケージ レベルの変数であっても、構造体のメンバー フィールドであっても問題ありません。
- 13 行目、一般に、共有アクセスの待ち時間を短縮するために、ミューテックスの粒度をできるだけ小さく設定することをお勧めします。ここで、作成者は習慣的にミューテックスの変数に次の形式で名前を付けています。
このミューテックスがこの変数を保護するために使用されることを示します。
- 16 行目は count の値を取得する関数パッケージです。これにより、変数 count に同時に安全にアクセスできます。
- 19 行目で、countGuard ミューテックスをロックしようとします。 countGuard がロックされると、別の goroutine がロックを継続しようとすると、countGuard のロックが解除されるまでブロックされます。
- 行 22 では、defer を使用して countGuard のロック解除を遅延させ、GetCount() 関数が返されたときにロック解除操作が行われます。
- 27 行目でカウント値を設定するとき、countGuard を使用してロックおよびロック解除操作を実行し、カウント値を変更するプロセスが同時アクセスの競合のないアトミックなプロセスであることを保証します。
読み取りが多く書き込みが少ない環境では、読み取り/書き込みミューテックス (sync.RWMutex) を最初に使用できます。これはミューテックスよりも効率的です。 sync パッケージの RWMutex は、読み取り/書き込みミューテックスのラッパーを提供します。
ミューテックスの例のコードの一部を読み取り/書き込みミューテックスに変更します。次のコードを参照してください。
var (
// Logicで使用される変数
count int
// 変数に対応するMutex
countGuard sync.RWMutex
)
func GetCount() int {
// ロックする
countGuard.RLock()
// 関数の終了時にアンロックする
defer countGuard.RUnlock()
return count
}
コードの説明は次のとおりです。
- 6 行目、countGuard を宣言するときに、sync.Mutex ミューテックスから sync.RWMutex 読み取り/書き込みミューテックスに変更します。
- 12行目のカウント取得処理は、カウントデータを読み出す処理であり、リードライトミューテックスに適しています。この行では、countGuard.Lock() を countGuard.RLock() に置き換えて、読み取り/書き込みミューテックスを読み取りとしてマークします。別のゴルーチンが同時に countGuard にアクセスし、同時に countGuard.RLock() を呼び出した場合、ブロックは発生しません。
- 行 15 は読み取りモードのロックに対応し、読み取りモードを使用してロックを解除します。