sync.Poolの挙動について(Golang)
概要
標準パッケージのsync.Pool
を利用して、ファイルの中身を常にメモリ上(=キャッシュ)におきたかったのですが、動作が不定のためうまく利用できなかった話です。
Go version
% go version go version go1.8.1 darwin/amd64
sync.Pool概要
概要としては、以下の認識です
- オブジェクトを一時的に、保持するための機能
- 再利用されるオブジェクトをキャッシュする利用法
- 複数のgoroutineで安全に利用可能
- copy禁止(
noCopy
structが埋め込まれている)
メソッド
Get
func (p *Pool) Get() interface{}
- Poolよりitemを取り出す
- 取り出されたitemはPoolから削除
- Putで入れた値とGetで取り出す値の関係を考慮すべきでない
Put
func (p *Pool) Put(x interface{})
- Poolに値を追加する
動作確認
下記のコードを利用して、Getした際とPutした際の挙動を確認してみました。
https://github.com/midorigreen/gopool/blob/master/main.go
package main import ( "fmt" "net/http" "sync" ) var pool = sync.Pool{ New: func() interface{} { return "init" }, } func main() { mux := http.NewServeMux() mux.HandleFunc("/pool", poolHandler) mux.HandleFunc("/pool/change", poolChangeHandler) http.ListenAndServe(":8888", mux) } func poolHandler(w http.ResponseWriter, r *http.Request) { fmt.Println("----------------------------------") fmt.Println("/pool") fmt.Println(get().(string)) fmt.Println("----------------------------------") } func poolChangeHandler(w http.ResponseWriter, r *http.Request) { fmt.Println("----------------------------------") s := get().(string) fmt.Println("/pool/change") pool.Put(s) fmt.Println("-- changed --") fmt.Println("----------------------------------") } func get() interface{} { s := pool.Get().(string) cpS := s // Getした値を再度Poolに戻している pool.Put(s) return cpS }
出力例
実際の出力例です。
上記コードより、get()
で呼び出した際は、Get→Put
としているため
同じ値がGet()
で取得されて欲しいところです。
ですが、一度/pool/change
APIを叩いた後も"init"が出力されていることから、
Get→Put
で値を戻したところで、必ず同じ値をGet()
してくれるわけではなさそうです。
---------------------------------- /pool init ---------------------------------- ---------------------------------- /pool/change -- changed -- ---------------------------------- # ↓ これ以降はすべて "change handler" が出力されていて欲しい ---------------------------------- /pool change handler ---------------------------------- ---------------------------------- /pool init ---------------------------------- ---------------------------------- /pool change handler ---------------------------------- ---------------------------------- /pool init ---------------------------------- ---------------------------------- /pool change handler ---------------------------------- ---------------------------------- /pool init ---------------------------------- ---------------------------------- /pool change handler ---------------------------------- ---------------------------------- /pool change handler ---------------------------------- ---------------------------------- /pool init ----------------------------------
感想
Callers should not assume any relation between values passed to Put and the values returned by Get. → Putで入れた値とGetで取り出す値の関係を考慮すべきでない
とあるように、Put()
で入れたからGet()
で必ず入れた値が取得できるわけではないことを念頭に置いて開発しないといけないようです。
ファイルの中身をキャッシュしておいて、書き換え時は更新等を行いたかったのですが、
更新をしたデータをPut()
で突っ込んでも返ってくるとは限らなくなってます。
結論、あまり良い使い方ではなさそうです。(一意にキャッシュを保てないので、実データと差分がでてしまうため)
補足
@nobonoboさんにも指摘頂きました。
Get処理でGet&Putしてるわけですね。Poolは内部にもつデータのどれをGetで返すのかは不定です。Put多数->Get多数と、Get&Putを繰り返す場合の挙動は異なるのでご注意を。
— nobonobo🐦 (@nobonobo) 2017年6月25日