こんにちは。
Go で thread-safe map を作れるか気になりました。複数の goroutine から同一キーへ同時にアクセスがあった場合に正しく queing させる方法でなくてはなりません。lock を最小にしたい場合、工夫が必要です。sync.Mutex を使って考えてみました。
package main
import (
"sync"
"fmt"
)
// map 構造(今回の例)
type Key int64
type Value struct {
e0 []int64
e1 []float64
e2 []string
}
// thread-safe map を作れるか?
var cmap = map[Key]Value{} // map 本体(今回は shard 分割していない)
var NUM_SHARD = 1024
var shards = []*sync.Mutex{}
var cmap_m []map[Key]*sync.Mutex
// e0 の値を put する例
func put_thread_safe (k Key, e0 int64) {
i := uint64(k)%uint64(NUM_SHARD) // shard id
shards[i].Lock() // shard へのアクセスをロック
if cmap_m[i][k] == nil {
cmap_m[i][k] = new(sync.Mutex)
}
cmap_m[i][k].Lock() // 他からの cmap へのアクセスは待たせる
shards[i].Unlock() // shard へのアクセスのロック解除
v := cmap[k] // 値をコピー
v.e0 = append(v.e0, e0) // 値を操作する例
cmap[k] = v // 書き戻す
cmap_m[i][k].Unlock() // 他からのアクセスのロック解除
}
func init_shard() {
for i := 0; i < NUM_SHARD; i++ {
shards = append(shards, new(sync.Mutex))
cmap_m = append(cmap_m, map[Key]*sync.Mutex{})
}
}
func main () {
init_shard()
x := make(map[Key][]int64)
x[37] = []int64{2, 4}
x[89] = []int64{2, 3, 5, 7}
for _, k := range []Key{37, 89} {
for _, e0 := range x[k] {
put_thread_safe(k, e0)
}
}
fmt.Println(cmap) // ==> map[37:{[2 4] [] []} 89:{[2 3 5 7] [] []}]
}