sync.PoolはGoで一時的なオブジェクトの再利用を促進し、ガベージコレクターへの負担を軽減するために導入されました。歴史的に、開発者はミューテックスを使って独自のオブジェクトプールを作成していましたが、これは複雑なコードとシンクロナイゼーションエラーを引き起こす可能性がありました。sync.Poolは、最初のアクセス時にオブジェクトを生成し、そのオブジェクトを保持して再利用のためにプールに戻すことを目的とした、標準ライブラリのスレッドセーフな構造体です。
問題は、大量の短命オブジェクト(例えば、HTTPサーバーのバッファ)を扱う際に、システムがアロケーションに多くの時間を費やすことです。sync.Poolを使用すると、オブジェクトをオプションでキャッシュでき、メモリにその保存を保証するわけではありませんが、ガベージコレクションの回数を減らすことでパフォーマンスを向上させます。
解決策は、一時的で均一な、頻繁に使用される構造体(例えば、bytes.Buffer)にプールを使用し、Putメソッドでオブジェクトをプールに戻し、Getメソッドで取り出すことです。オブジェクトはいつでもプールから削除される可能性があるため(例えば、GCが実行される際)、長期的な保存には適しません。
コード例:
import ( "sync" "bytes" "fmt" ) var bufPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, } func main() { b := bufPool.Get().(*bytes.Buffer) b.Reset() b.WriteString("hello, world!") fmt.Println(b.String()) bufPool.Put(b) // 必ず返却する }
主な特徴:
sync.Poolは状態を長期保存するために使えるか?
いいえ、プール内のオブジェクトは、システムによっていつでも削除される可能性があります(GCまたは負荷の減少時に)。プールはgoroutine間の一時的なストレージのためにのみ設計されています。
プールは以前に置かれたオブジェクトを正確に返却することを保証しますか?
いいえ。プールは適切なオブジェクトを返すか、必要に応じて新しいオブジェクトを生成します。ユーザーとオブジェクトプールとの間にバインディングを持つべきではありません。
オブジェクトをプールに戻す前にクリア/リセットする必要がありますか?
はい、そうしないと次のスレッドが前のデータの残りを持つ「汚い」オブジェクトを受け取る可能性があります。
ネガティブケース
開発者がチャットに再接続するためにクライアントの状態をプールに保存します。GCが起動すると接続が失われ、ユーザーがデータを失います。
利点:
欠点:
ポジティブケース
JSONのmarshal/unmarshal用のBufferオブジェクトをプールに入れて、メッセージのバッチ処理に使用します。処理後、オブジェクトをクリアしてプールに戻し、アロケーションの数を減らします。
利点:
欠点: