Double Checked Lockとは?
Double-checked lockingとは、通常は複数スレッドから参照される変数を高速、かつ安全に初期化するために利用される手法です。
- 初期化処理は一回しか動かしたくない→ロックが必要
- 初期化中のインスタンスは利用できない→ロックが必要
- 初期化完了後はできるかぎり高速に動作させたい→ロックしたくない
ロックが必要だけど、ロックしたくない。この相反する要求を満たすために、ロックをせずにまずは確認を行い、初期化が必要なときにはロックを取得して再度確認を行う。2回確認するのでDouble Checkedと呼ぶわけです。
TypeScriptなら不要では?
通常シングルスレッドで動作するTypeScript環境ですが、非同期処理が存在します。複数の非同期処理から参照される変数を高速、かつ安全に初期化するためにはこの手法が有効になります。
実際のコードは下記のようになります。
export class LazyInitializer<T> {
protected _target: T | undefined
protected _synchronizer = new Semaphore(1)
constructor(protected readonly _factory: () => Promise<T>) {
}
async get(): Promise<T> {
if (this._target !== undefined) {
return this._target
}
await this._synchronizer.synchronized(async () => {
if (this._target === undefined) {
this._target = await this._factory()
}
})
// this._target cannot be undefined unless factory returns undefined
if (this._target === undefined) {
throw new SynchronizerInvalidError("factory returned undefined")
}
return this._target
}
}
注:TypeScriptには標準でSemaphoreは存在しないので、Semaphoreは別途実装が必要です。
https://qiita.com/tomohisaota/items/88220c5812b1281e0985
自作ライブラリ ya-syn
今回紹介した実装はya-synの実装の一部になっています。
もちろんSemaphoreもあります!