昨日GoCon2014がありまして、発表の機会を頂いたのですが発表後に @rui314さんから指摘のあったグローバル変数への読み込み・書き込みの競合の問題、これに関してはsync.RWMutexを使うのがいいので、使ってみました。
RLockとLock
通常のsync.Mutex
はLockのみしか提供しない。これをRead, Write双方で使ってみると非常に効率が悪くなります。そこでsync.RWMutex
はRLockというもう一つのLockを提供しています。
この2つの違いは、
- RLock: Read向けのLock。RLock同士はブロックせず、Lockのみがブロックされる。解除時はUnlockではなくRUnlockする。
- Lock: いわゆる普通のLock。RLock, Lock双方をブロックする。
上記を使い分けることで効率よくLockを行えるようです。
使ってみる
実際にどう動くのか見るのが分かりやすいと思います。
play.golang.org便利なので貼っときます。
http://play.golang.org/p/JJH3Tm8Sl7
package main
import (
"sync"
"time"
)
var mu sync.RWMutex
var data map[string]string
func main() {
data = map[string]string{"hoge": "fuga"}
mu = sync.RWMutex{}
go read()
go read()
go write()
go read()
time.Sleep(5 * time.Second)
}
func read() {
println("read_start")
mu.RLock()
defer mu.RUnlock()
time.Sleep(1*time.Second)
println("read_complete", data["hoge"])
}
func write() {
println("write_start")
mu.Lock()
defer mu.Unlock()
time.Sleep(2 * time.Second)
data["hoge"] = "piyo"
println("write_complete")
}
$ go run rwmutex.go
read_start
read_start
write_start
read_start
read_complete fuga
read_complete fuga
write_complete
read_complete piyo
最初のread_complete fuga
の部分に関しては、RLockしているので、ほぼ同時に表示されます。
その後write->最後のreadの順にロックが解除されていって実行されています。
上記コードのRLockを全てLock, RUnlockをUnlockに書き換えると挙動の違いが見て分かりやすいかと思います。
http://play.golang.org/p/-w_V4l5K2e