キューを使用するスピン ロック
エンキューされたスピン ロックは、非常に競合の激しいロックに適したスピン ロックのバリアントです。 従来のキューに入れていないスピン ロックは、軽く結び付いたり、期間を短くしたりする場合に適しています。
キューに入ったスピン ロックを使用する利点は次のとおりです。
プロセッサの競合の減少: 従来のスピン ロックは、複数のスレッドがロックの状態をチェックループ (または "スピン") するため、ロックを同時に取得しようとすると、プロセッサの競合が大きくなる可能性があります。 これにより、特にマルチプロセッサ システムでは、システムのパフォーマンスが低下する可能性があります。 キューに入ったスピン ロックは、スレッドをキューに編成することでこれを軽減します。 スレッドがロックを取得すると、次の行だけがアクティブに回転し、ロックの取得を待機します。 これにより、特にロックが長時間保持されている場合に、スピンに無駄な CPU サイクルが減ります。
公平性と飢瞞の回避: 基本的なスピン ロックの問題の 1 つは公平性の欠如です。スレッドは枯渇し、他のスレッドが継続的に取得して解放している場合はロックを取得できません。 キューに登録されたスピン ロックは、スレッドが試行した順序でロックを取得することで、この問題に対処します。 このシーケンシャル処理により、不足が回避され、すべてのスレッドが時間の経過と同時に確実に処理されるようになります。
スケーラビリティ: システムでプロセッサまたはコアの数が増えるにつれて、同期メカニズムの効率がパフォーマンスにとって重要になります。 キューに置かれたスピン ロックは、すべてのコアでアクティブなスピンを最小限に抑えることでプロセッサのオーバーヘッドを削減するため、従来のスピン ロックよりもスケーラブルです。 これは、ドライバーの効率がシステム全体のパフォーマンスに直接影響を与える可能性がある、高パフォーマンスのマルチコア システムで特に重要です。
システム リソースの効率的な使用: 不要なプロセッサのスピンを減らすことで、キューに入れられたスピン ロックにより、システムはリソースをより効率的に使用できます。 これにより、デバイス ドライバーのパフォーマンスが向上するだけでなく、システムの全体的な応答性と電力消費量にもプラスの影響を与えます。これは、電力に敏感な環境で特に有益です。
シンプルさと信頼性: 競合を減らし、公平性を向上させる利点があるにもかかわらず、キューに入れたスピン ロックは開発者から複雑さを抽象化します。 開発者が複雑なロック ロジックを実装しなくても、共有リソースを保護するためのシンプルで信頼性の高いメカニズムが提供されます。 これにより、不適切なロック処理に関連するバグの可能性が低くなるため、ドライバーの信頼性が向上します。
Windows カーネル モード ドライバーで、キューに入ったスピン ロックを使用して説明されている操作を示す簡略化されたコード スニペットを次に示します。 この例では、KeInitializeSpinLock を使用してスピン ロックを宣言して初期化し、それぞれ KeAcquireInStackQueuedSpinLock と KeReleaseInStackQueuedSpinLock を使用してロックを取得して解放する方法を示します。
KSPIN_LOCK SpinLock;
KLOCK_QUEUE_HANDLE LockHandle;
// Initialize the spin lock
KeInitializeSpinLock(&SpinLock);
// Assume this function is called in some kind of context where
// the below operations make sense, e.g., in a device I/O path
// Acquire the queued spin lock
KeAcquireInStackQueuedSpinLock(&SpinLock, &LockHandle);
// At this point, the current thread holds the spin lock.
// Perform thread-safe operations here.
// ...
// Release the queued spin lock
KeReleaseInStackQueuedSpinLock(&LockHandle);
ドライバーは KLOCK_QUEUE_HANDLE 構造体を確保し、ポインターを使用して KeAcquireInStackQueuedSpinLockに渡します。 ドライバーは、スピン ロックを解放するときに、KeReleaseInStackQueuedSpinLock へのポインターで同じ構造体を渡します。
ドライバーは通常、ロックを取得するたびにスタックに構造体を割り当てる必要があります。 ドライバーに、デバイス コンテキストの一部として構造体を割り当てさせないでください。また、複数のスレッドから同じ構造体を共有させないでください。
キューに置かれたスピン ロック ルーチンに対する呼び出しと、同じスピン ロック上の通常の KeXxxSpinLock ルーチンへの呼び出しをドライバーで混在させないでください。
ドライバーが既に IRQL = DISPATCH_LEVEL にある場合は、ドライバーは代わりに KeAcquireInStackQueuedSpinLockAtDpcLevel と KeReleaseInStackQueuedSpinLockFromDpcLevel を呼び出すことができます。