クリティカル セクションとミューテックスはオペレーティング システム固有のものではなく、マルチスレッド/マルチプロセッシングの概念です。
クリティカル セクション 任意の時点で単独でのみ実行する必要があるコードの一部です (たとえば、同時に実行される 5 つのスレッドと、配列を更新する「critical_section_function」と呼ばれる関数があります...5 つのスレッドすべてがそのため、プログラムが critical_section_function() を実行している場合、他のどのスレッドも critical_section_function を実行してはなりません。
ミューテックス* Mutex は、クリティカル セクション コードを実装する方法です (トークンのようなものと考えてください... critical_section_code を実行するには、スレッドがこれを所有している必要があります)
Windows の場合、クリティカル セクションはミューテックスより軽量です。
ミューテックスはプロセス間で共有できますが、常にオーバーヘッドのあるカーネルへのシステム コールが発生します。
クリティカル セクションは 1 つのプロセス内でのみ使用できますが、競合が発生した場合にのみカーネル モードに切り替わるという利点があります。一般的なケースである非競合取得は、信じられないほど高速です。競合が発生した場合、それらはカーネルに入り、何らかの同期プリミティブ (イベントやセマフォなど) を待機します。
この 2 つの時間を比較する簡単なサンプル アプリを作成しました。私のシステムでは、1,000,000 回の非競合の取得と解放を行うと、ミューテックスに 1 秒以上かかります。クリティカル セクションは、1,000,000 回の取得に約 50 ミリ秒かかります。
テスト コードは次のとおりです。これを実行したところ、ミューテックスが 1 番目または 2 番目の場合に同様の結果が得られたため、他の影響は見られません。
HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
CRITICAL_SECTION critSec;
InitializeCriticalSection(&critSec);
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
LARGE_INTEGER start, end;
// Force code into memory, so we don't see any effects of paging.
EnterCriticalSection(&critSec);
LeaveCriticalSection(&critSec);
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
EnterCriticalSection(&critSec);
LeaveCriticalSection(&critSec);
}
QueryPerformanceCounter(&end);
int totalTimeCS = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);
// Force code into memory, so we don't see any effects of paging.
WaitForSingleObject(mutex, INFINITE);
ReleaseMutex(mutex);
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
WaitForSingleObject(mutex, INFINITE);
ReleaseMutex(mutex);
}
QueryPerformanceCounter(&end);
int totalTime = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);
printf("Mutex: %d CritSec: %d\n", totalTime, totalTimeCS);
他の回答に加えて、次の詳細はウィンドウの重要なセクションに固有のものです:
- 競合がなければ、クリティカル セクションの取得は
InterlockedCompareExchange
と同じくらい簡単です 操作 - クリティカル セクション構造にはミューテックスの余地があります。最初は割り当てられていません
- クリティカル セクションのスレッド間で競合が発生した場合、ミューテックスが割り当てられて使用されます。クリティカル セクションのパフォーマンスはミューテックスのパフォーマンスに低下します
- 高い競合が予想される場合は、スピン カウントを指定してクリティカル セクションを割り当てることができます。
- クリティカル セクションでスピン カウントの競合が発生した場合、クリティカル セクションを取得しようとするスレッドは、その数のプロセッサ サイクルの間スピン (ビジー待機) します。別のスレッドへのコンテキスト スイッチを実行するためのサイクル数は、所有しているスレッドがミューテックスを解放するために必要なサイクル数よりもはるかに多くなる可能性があるため、これにより、スリープ状態よりもパフォーマンスが向上する可能性があります
- スピン カウントが期限切れになると、ミューテックスが割り当てられます
- 所有スレッドがクリティカル セクションを解放するときは、mutex が割り当てられているかどうかを確認する必要があります。割り当てられている場合は、mutex を設定して待機中のスレッドを解放します
Linux では、スピン カウントを持つクリティカル セクションと同様の目的を果たす「スピン ロック」があると思います。
理論的な観点から、クリティカル セクションは、コードが共有リソースにアクセスするため、一度に複数のスレッドで実行してはならないコードです。
ミューテックスは、重要なセクションを保護するために使用されるアルゴリズム (および場合によってはデータ構造の名前) です。
セマフォとモニターはミューテックスの一般的な実装です。
実際には、Windows で利用可能な多くのミューテックス実装があります。それらは主に、実装の結果として、ロックのレベル、スコープ、コスト、およびさまざまなレベルの競合下でのパフォーマンスによって異なります。さまざまなミューテックス実装のコストのグラフについては、「CLR Inside Out -Using concurrency for scalability」を参照してください。
利用可能な同期プリミティブ。
- 監視
- ミューテックス
- セマフォ
- ReaderWriterLock
- ReaderWriterLockSlim
- 連動
lock(object)
ステートメントは Monitor
を使用して実装されます - MSDN を参照してください。
ここ数年、ノンブロッキング同期について多くの研究が行われてきました。目標は、ロックフリーまたはウェイトフリーの方法でアルゴリズムを実装することです。このようなアルゴリズムでは、プロセスは他のプロセスが作業を終了できるように支援し、プロセスが最終的に作業を終了できるようにします。結果として、何らかの作業を実行しようとした他のプロセスがハングした場合でも、プロセスはその作業を終了できます。ロックを使用すると、ロックを解放せず、他のプロセスの続行を妨げません。