Critical Section Objects (クリティカル セクション オブジェクト)
クリティカル セクション オブジェクトは、ミューテックス オブジェクトで提供される同期と同様の同期を提供します。ただし、クリティカル セクションは 1 つのプロセスのスレッドでのみ使用できます。 クリティカル セクション オブジェクトをプロセス間で共有することはできません。
イベント、ミューテックス、およびセマフォ オブジェクトは、単一プロセス アプリケーションでも使用できますが、クリティカル セクション オブジェクトは、相互排他同期 (プロセッサ固有のテストと設定命令) に対して、やや高速で効率的なメカニズムを提供します。 ミューテックス オブジェクトと同様に、クリティカル セクション オブジェクトは一度に 1 つのスレッドのみが所有できるため、共有リソースを同時アクセスから保護するのに役立ちます。 ミューテックス オブジェクトとは異なり、クリティカル セクションが破棄されたかどうかを判断する方法はありません。
Windows Server 2003 Service Pack 1 (SP1) 以降では、クリティカル セクションで待機しているスレッドは、先着順でクリティカル セクションを取得しません。 この変更により、ほとんどのコードのパフォーマンスが大幅に向上します。 ただし、一部のアプリケーションは先入れ先出し (FIFO) の順序に依存しており、現在のバージョンの Windows (たとえば、レートリミッターとして重要なセクションを使用しているアプリケーション) でパフォーマンスが低下するか、まったく実行されない場合があります。 コードが正常に動作し続けるには、同期のレベルを追加する必要がある場合があります。 たとえば、プロデューサー スレッドとコンシューマー スレッドがあり、クリティカル セクション オブジェクトを使用して作業を同期しているとします。 2 つのイベント オブジェクトを作成します。1 つは、もう一方のスレッドが続行する準備ができていることを通知するために使用するスレッドごとに 1 つです。 コンシューマー スレッドは、プロデューサーがクリティカル セクションに入る前にイベントを通知するのを待機し、プロデューサー スレッドはコンシューマー スレッドがクリティカル セクションに入る前にイベントを通知するのを待機します。 各スレッドがクリティカル セクションから離れると、そのイベントにもう一方のスレッドを解放するように通知されます。
Windows Server 2003 および Windows XP: クリティカル セクションで待機しているスレッドは、待機キューに追加されます。これらは起こされ、通常はキューに追加された順序でクリティカル セクションを取得します。 ただし、スレッドがこのキューに十分な速度で追加されると、各待機スレッドの起動にかかる時間が原因でパフォーマンスが低下する可能性があります。
プロセスは、クリティカル セクションによって使用されるメモリを割り当てる役割を担います。 これは通常、 CRITICAL_SECTION型の変数を宣言するだけで行われます。 プロセスのスレッドで使用する前に、 InitializeCriticalSection または InitializeCriticalSectionAndSpinCount 関数を使用してクリティカル セクション を 初期化します。
スレッドは 、EnterCriticalSection 関数または TryEnterCriticalSection 関数を使用して、クリティカル セクションの所有権を要求します。 LeaveCriticalSection 関数を使用して、クリティカル セクションの所有権を解放します。 クリティカル セクション オブジェクトが現在別のスレッドによって所有されている場合、 EnterCriticalSection は所有権を無期限に待機します。 これに対し、ミューテックス オブジェクトを相互除外に使用する場合、 待機関数 は指定されたタイムアウト間隔を受け入れます。 TryEnterCriticalSection 関数は、呼び出し元のスレッドをブロックせずにクリティカル セクションの入力を試みます。
スレッドがクリティカル セクションを所有している場合、実行をブロックすることなく 、EnterCriticalSection または TryEnterCriticalSection を追加で呼び出すことができます。 これにより、既に所有している重要なセクションを待機している間にスレッド自体がデッドロックするのを防ぐことができます。 その所有権を解放するには、スレッドがクリティカル セクションに入るたびに LeaveCriticalSection を 1 回呼び出す必要があります。 待機中のスレッドがクリティカル セクションの所有権を取得する順序に関する保証はありません。
スレッドは InitializeCriticalSectionAndSpinCount または SetCriticalSectionSpinCount 関数を使用して、クリティカル セクション オブジェクトのスピンカウントを指定します。 スピンとは、スレッドがロックされているクリティカル セクションを取得しようとしたときに、スレッドがループに入り、ロックが解放されているかどうかを確認し、ロックが解除されていない場合はスレッドがスリープ状態になるということです。 単一プロセッサ システムでは、スピン数は無視され、クリティカル セクションのスピン数は 0 (ゼロ) に設定されます。 マルチプロセッサ システムでは、クリティカル セクションが使用できない場合、呼び出し元のスレッドは dwSpinCount 回スピンしてから、クリティカル セクションに関連付けられているセマフォに対して待機操作を実行します。 スピン操作中にクリティカル セクションが解放されると、呼び出し元スレッドは待機操作を回避します。
プロセスのすべてのスレッドは 、DeleteCriticalSection 関数を使用して、クリティカル セクション オブジェクトが初期化されたときに割り当てられるシステム リソースを解放できます。 この関数が呼び出されると、クリティカル セクション オブジェクトを同期に使用できません。
クリティカル セクション オブジェクトが所有されている場合、影響を受ける他のスレッドは、 EnterCriticalSection の呼び出しで所有権を待機しているスレッドだけです。 待機していないスレッドは、自由に実行を続行できます。
関連トピック