One-Time 初期化
多くの場合、コンポーネントは、読み込まれるときにではなく、最初に呼び出されたときに初期化タスクを実行するように設計されています。 1 回限りの初期化関数を使用すると、複数のスレッドが初期化を試みる場合でも、この初期化は 1 回だけ実行されます。
Windows Server 2003 および Windows XP: アプリケーションは、のインターロックされた機能 またはその他の同期メカニズムを使用して、1 回限りの初期化に独自の同期を提供する必要があります。 1 回限りの初期化関数は、Windows Vista および Windows Server 2008 以降で使用できます。
1 回限りの初期化関数は、初期化を実行するスレッドが 1 つだけであることを保証する重要な利点を提供します。
- それらは速度のために最大限に活用される。
- 必要なプロセッサ アーキテクチャに適切なバリアを作成します。
- ロックされた初期化と並列初期化の両方がサポートされます。
- コードが非同期的または同期的に動作できるように、内部ロックを回避します。
システムは、データと状態情報を含む不透明な INIT_ONCE 構造体を使用して初期化プロセスを管理します。 呼び出し元は、この構造体を割り当て、InitOnceInitialize を呼び出すか (構造体を動的に初期化する)、または構造体変数に定数 INIT_ONCE_STATIC_INIT を割り当てます (構造体を静的に初期化するため)。 最初は、1 回限りの初期化構造体に格納されているデータは NULL で、その状態は初期化されていません。
1 回限りの初期化構造体をプロセス間で共有することはできません。
初期化を実行するスレッドは、必要に応じて、初期化の完了後に呼び出し元が使用できるコンテキストを設定できます。 コンテキストは、同期オブジェクトにすることも、値またはデータ構造にすることもできます。 コンテキストが値の場合、下位 INIT_ONCE_CTX_RESERVED_BITS は 0 である必要があります。 コンテキストがデータ構造の場合、データ構造は DWORD -alignedする必要があります。 コンテキストは、InitOnceBeginInitialize または initOnceExecuteOnce関数の lpContext 出力パラメーター呼び出し元に返されます。
1 回限りの初期化は、同期的または非同期的に実行できます。 オプションのコールバック関数は、同期的な 1 回限りの初期化に使用できます。
同期的な 1 回限りの初期化
次の手順では、コールバック関数を使用しない同期的な 1 回限りの初期化について説明します。
- InitOnceBeginInitialize 関数を呼び出す最初のスレッドでは、1 回限りの初期化が正常に開始されます。 同期的な 1 回限りの初期化の場合は、INIT_ONCE_ASYNC フラグなしで InitOnceBeginInitialize を呼び出す必要があります。
- 初期化を試みる後続のスレッドは、最初のスレッドが初期化を完了するか失敗するまでブロックされます。 最初のスレッドが失敗した場合、次のスレッドは初期化の試行などを許可されます。
- 初期化が完了すると、スレッドは InitOnceComplete 関数を呼び出します。 スレッドは、必要に応じて同期オブジェクト (またはその他のコンテキスト データ) を作成し、InitOnceComplete 関数の lpContext パラメーターでそれを指定できます。
- 初期化が成功すると、1 回限りの初期化構造体の状態が初期化に変更され、lpContext ハンドル (ある場合) が初期化構造体に格納されます。 以降の初期化の試行では、このコンテキスト データが返されます。 初期化に失敗した場合、データは NULL 。
次の手順では、コールバック関数を使用する同期的な 1 回限りの初期化について説明します。
- InitOnceExecuteOnce 関数を正常に呼び出す最初のスレッドは、アプリケーション定義の InitOnceCallback コールバック関数に必要なすべてのデータへのポインターを渡します。 呼び出しが成功すると、InitOnceCallback コールバック関数が実行されます。
- 初期化を試みる後続のスレッドは、最初のスレッドが初期化を完了するか失敗するまでブロックされます。 最初のスレッドが失敗した場合、次のスレッドは初期化の試行などを許可されます。
- 初期化が完了すると、コールバック関数が返されます。 コールバック関数は、必要に応じて同期オブジェクト (またはその他のコンテキスト データ) を作成し、Context 出力パラメーターでそれを指定できます。
- 初期化が成功すると、1 回限りの初期化構造体の状態が初期化に変更され、コンテキスト ハンドル (存在する場合) が初期化構造体に格納されます。 以降の初期化の試行では、このコンテキスト データが返されます。 初期化に失敗した場合、データは NULL 。
非同期の 1 回限りの初期化
次の手順では、非同期の 1 回限りの初期化について説明します。
- initOnceBeginInitialize を INIT_ONCE_ASYNCで呼び出して複数のスレッドが同時に初期化を開始しようとすると、fPending パラメーターが TRUE に設定されているすべてのスレッドに対して関数成功します。 初期化時に実際に成功するのは 1 つのスレッドだけです。その他の同時試行では、初期化状態は変更されません。
- InitOnceBeginInitialize戻ると、fPending パラメーターは初期化状態を示します。
- fPending FALSE 場合、初期化時に 1 つのスレッドが成功しました。 他のスレッドは、作成したコンテキスト データをクリーンアップし、initOnceBeginInitializeの lpContext 出力パラメーター内のコンテキスト データ使用する必要があります。
- fPending が TRUE 場合、初期化はまだ完了していないため、他のスレッドを続行する必要があります。
- 各スレッドは、InitOnceComplete 関数を呼び出します。 スレッドは、必要に応じて同期オブジェクト (またはその他のコンテキスト データ) を作成し、initOnceComplete の lpContext パラメーター指定できます。
- InitOnceCompleteが返されるときに、その戻り値は、呼び出し元のスレッドが初期化時に成功したかどうかを示します。
- InitOnceComplete成功した場合、呼び出し元のスレッドは初期化時に成功しました。 1 回限りの初期化構造体の状態が初期化に変更され、lpContext ハンドル (ある場合) が初期化構造体に格納されます。
- InitOnceComplete失敗した場合、初期化時に別のスレッドが成功しました。 呼び出し元のスレッドは、作成したすべてのコンテキスト データをクリーンアップし、INIT_ONCE_CHECK_ONLYInitOnceBeginInitialize を呼び出して、1 回限りの初期化構造体に格納されているすべてのコンテキスト データを取得する必要があります。
複数のサイトからの One-Time 初期化の呼び出し
1 つの INIT_ONCE 構造体によって保護された 1 回限りの初期化は、複数のサイトから実行できます。各サイトから異なるコールバックが渡される場合があり、コールバックの有無にかかわらず同期が混在する場合があります。 初期化は引き続き 1 回だけ正常に実行することが保証されます。
ただし、非同期初期化と同期初期化を混在させることはできません。非同期初期化が試行されると、同期初期化を開始しようとすると失敗します。
関連トピック
-
One-Time 初期化 を使用した