2009-04-29 26 views
112

Linux、Windowsの観点から説明してください。ミューテックスとクリティカルセクションの違いは何ですか?

私はC#でプログラミングしていますが、これらの2つの用語が違いますか?例と、そのような....

感謝のWindowsでは

+9

なぜこのタグは 'language-agnostic' *と*' c# 'ですか? – Puppy

+4

十分な担当者がいるかどうかコメントした人と、可能であれば5人を上回った人のための良い読書:http://blogs.msdn.com/b/oldnewthing/archive/2012/10/17/10360184.aspx –

+0

素晴らしいリンク@ BrianR.Bondyありがとう。 – Wodzu

答えて

194

Windowsの場合、クリティカルセクションはミューテックスよりも軽量です。

ミューテックスはプロセス間で共有できますが、カーネルへのシステムコールは常にオーバーヘッドがあります。

クリティカルセクションは1つのプロセス内でのみ使用できますが、競合の場合はカーネルモードに切り替えるという利点があります。一般的なケースでなければならない無条件取得は信じられないほど高速です。競合の場合、カーネルに入り、(イベントやセマフォのような)いくつかの同期プリミティブを待つ。

私は、それらの2つの間の時間を比較するクイックサンプルアプリを作成しました。私のシステムでは、1,000,000回の無条件取得と解放のために、mutexが1秒以上かかる。クリティカルセクションでは、1,000,000回の取得で約50ミリ秒かかります。

ここにテストコードがありますが、私はこれを実行し、mutexが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); 
+0

は私を打ち負かす - あなたのコードを投稿するべきかもしれない。あなたが気分が良くなるなら、私はあなたに1点をあげました。 –

+1

よくできました。 Upvoted。 – ApplePieIsGood

+1

これが関連しているかどうかはわかりません(コードをコンパイルして試していないため)が、INFINITEのWaitForSingleObjectを呼び出すとパフォーマンスが低下することがわかりました。タイムアウト値を1にすると、戻り値をチェックしながらループすると、私のコードのパフォーマンスが大きく変わってしまいます。しかし、これは主に外部のプロセスハンドルを待つという文脈の中にあります。ミューテックスではありません。 YMMV。私はミューテックスがどのようにその修正を行うのか見てみたいと思うだろう。このテストとの結果の時間差は、予想されるよりも大きいようです。 –

6

で、クリティカルセクションは、あなたのプロセスに対してローカルである、あなたはできる限りを投稿してください。ミューテックスは、プロセス間で共有/アクセスすることができます。基本的にクリティカルセクションは非常に安価です。特にLinuxではコメントできませんが、一部のシステムでは同じことのエイリアスです。

12

mutexは、スレッドが獲得できるオブジェクトであり、他のスレッドが獲得できないオブジェクトです。それは勧告であり、必須ではない。スレッドは、ミューテックスがそれを取得せずに表すリソースを使用できます。

クリティカルセクションは、オペレーティングシステムによってインターラプトされないことが保証されているコードの長さです。擬似コードでは、それは次のようであろう:

StartCriticalSection(); 
    DoSomethingImportant(); 
    DoSomeOtherImportantThing(); 
EndCriticalSection(); 
+2

私は間違っていますか?ダウンした有権者が理由を述べるなら、私はそれを感謝するだろう。 – Zifre

+0

+1は投票が私を混乱させるためです。 :pこれは、MutexとCritical Sectionがマルチスレッドのための2つの異なるメカニズムであることを暗示する記述よりも正しいと言えます。クリティカルセクションは、1つのスレッドだけがアクセスすべきコードセクションです。ミューテックスを使用することは、クリティカルセクションを実装する1つの方法です。 –

+1

私はポスターがWin32クリティカルセクションオブジェクトのようなユーザーモード同期プリミティブについて話していたと思いますが、これは単に相互排除を提供するだけです。 Linuxについてはわかりませんが、Windowsカーネルには、あなたのように振る舞う重要な領域があります。 – Michael

17

クリティカルセクションとミューテックスは、マルチスレッド/マルチプロセッシングシステムの特定、それらの概念を動作していません。

クリティカルセクション は、任意の時点で、それ自身で実行しなければならないコードの一部(例えば、5つの同時実行中のスレッドと配列を更新し、「critical_section_function」と呼ばれる機能があります...あなたですプログラムはcritical_section_functionを()実行しているときに、他のスレッドのどれもが自分のcritical_section_functionを実行してはならない。一度、配列を更新し、すべての5つのスレッドを望んでいない。

ミューテックス* ミューテックスがクリティカルを実装する方法ですセクションコード(それはトークンのように考える...スレッドはcritical_section_codeを実行するためにそれを所有していなければならない)

+1

また、mutexはプロセス間で共有できます。 – configurator

18

、次の詳細は、Windows上のクリティカルセクションに固有のものです:

  • 競合が存在しない場合に、クリティカルセクションを取得することInterlockedCompareExchange操作
  • のと同じくらい簡単です
  • クリティカルセクション構造はミューテックスのための空間を保持します。最初は割り当てられていません
  • クリティカルセクションのスレッド間に競合がある場合、ミューテックスが割り当てられ、使用されます。重大な競合が予想される場合は、クリティカルセクションのパフォーマンスがミューテックスのパフォーマンスに低下します。
  • スピンカウントを指定するクリティカルセクションを割り当てることができます。
  • スピンカウントのクリティカルセクションに競合がある場合、クリティカルセクションを取得しようとするスレッドは、その多くのプロセッササイクルでスピン(ビジー待機)します。これにより、スリープよりもパフォーマンスが向上します。別のスレッドへのコンテキスト切り替えを実行するサイクル数が、所有スレッドがミューテックスをリリースするために取ったサイクル数よりもはるかに多い可能性があります。
  • スピンカウントが満了すると、ミューテックスは、所有しているスレッドがクリティカルセクションを解放したとき、それはそれからであれば、それはLinuxでは待機中のスレッド

を解放するためにmutexを設定します、mutexが割り当てられているかどうかを確認するために必要とされる

  • 割り当てられますスピンカウントでクリティカルセクションと同様の目的を果たす「スピンロック」を持っていると思います。

  • +0

    残念なことに、ウィンドウクリティカルセクションでは、実際のインターロック操作よりもはるかに高価なカーネルモード*でCAS操作*を行う必要があります。また、Windowsのクリティカルセクションでは、スピンカウントを関連付けることができます。 – Promit

    +1

    それは確かに真実ではありません。 CASは、ユーザモードでcmpxchgを使用して実行できます。 – Michael

    +0

    InitializeCriticalSectionを呼び出した場合、デフォルトのスピン数が0であると考えました。スピンカウントを適用するには、InitializeCriticalSectionAndSpinCountを呼び出す必要があります。あなたはそれについての参考文献を持っていますか? –

    71

    理論的には、critical sectionは、コードが共有リソースにアクセスするため、一度に複数のスレッドで実行してはならないコードです。

    mutexは、クリティカルセクションを保護するために使用されるアルゴリズム(および時にはデータ構造の名前)です。

    SemaphoresおよびMonitorsは、ミューテックスの一般的な実装です。

    実際には、ウィンドウには多数のミューテックス実装が用意されています。彼らは主に、ロックのレベル、スコープ、コスト、および異なるレベルの競合下でのパフォーマンスによって、実装の結果として異なります。異なるmutex実装のコストのチャートについては、CLR Inside Out - Using concurrency for scalabilityを参照してください。

    利用可能な同期プリミティブ。

    lock(object)文はimplemenですMonitorを使用してください - 参考としてMSDNを参照してください。

    最後に、多くの研究がnon-blocking synchronizationで行われています。目標は、ロックフリーまたはウェイトフリーの方法でアルゴリズムを実装することです。このようなアルゴリズムでは、プロセスが他のプロセスの作業を終了させ、プロセスが最終的に作業を完了できるようにします。結果的に、プロセスは、何らかの作業を実行しようとした他のプロセスがハングしても、その作業を終了することができます。ロックを使用すると、ロックを解放せず、他のプロセスが続行されないようにします。

    +0

    受け入れられた答えを見て、あなたが書き込んだ** Theoretical Perspective **を見るまで、私は重大なセクションのコンセプトを間違って思い出したかもしれないと思っていました。 :) –

    +1

    _Practical_ロックフリープログラミングは、それが存在することを除いて、シャングリラのようなものです。 Keir Fraserの[論文](http://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-579.pdf)(PDF)は、これをむしろ興味深く探求している(2004年に戻る)。私たちはまだ2012年にそれを苦労しています。我々は吸う。 –

    +0

    この回答は本当に良いです。私はそれが好きです。 –

    5

    私の2セントを追加するだけで、クリティカルセクションは構造として定義され、それらの操作はユーザーモードのコンテキストで実行されます。

    ntdll!_RTL_CRITICAL_SECTION 
        +0x000 DebugInfo  : Ptr32 _RTL_CRITICAL_SECTION_DEBUG 
        +0x004 LockCount  : Int4B 
        +0x008 RecursionCount : Int4B 
        +0x00c OwningThread  : Ptr32 Void 
        +0x010 LockSemaphore : Ptr32 Void 
        +0x014 SpinCount  : Uint4B 
    

    mutexは、Windowsオブジェクトディレクトリに作成されたカーネルオブジェクト(ExMutantObjectType)です。ミューテックス操作は、ほとんどがカーネルモードで実装されています。たとえば、ミューテックスを作成すると、カーネルにnt!NtCreateMutantを呼び出すことになります。

    +0

    Mutexオブジェクトを初期化して使用するプログラムがクラッシュするとどうなりますか?ミューテックスオブジェクトは自動的に割り当て解除されますか?いいえ、私は言うでしょう。右? – Ankur

    +4

    カーネルオブジェクトには参照カウントがあります。オブジェクトへのハンドルを閉じると参照カウントが減少し、0になるとオブジェクトは解放されます。プロセスがクラッシュすると、そのハンドルはすべて自動的に閉じられ、そのプロセスだけがハンドルを持つmutexは自動的に割り当てが解除されます。 – Michael

    11

    Linuxでの「高速」Windowsクリティカル選択は、futexとなり、高速のユーザースペースミューテックスを表します。フューテックスとミューテックスの違いは、フューテックスでは、アービトレーションが必要なときにのみカーネルが関与するため、原子カウンタが変更されるたびにカーネルと会話するオーバーヘッドを節約できます。フューテックスは、ミューテックスを共有する手段を使用して、プロセス間で共有することもできます。

    残念ながら、フューテクスはvery tricky to implement(PDF)です。

    これを超えると、両方のプラットフォームでほぼ同じです。飢餓を起こさないように、共有構造にアトミックなトークン駆動の更新を行います。残っているのは、単にそれを達成する方法です。

    0

    マイケルからの偉大な答え。 C++ 11で導入されたmutexクラスの3番目のテストを追加しました。結果はやや面白いですが、依然として単一プロセスのCRITICAL_SECTIONオブジェクトの元の裏付けをサポートしています。

    mutex m; 
    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); 
    
    // Force code into memory, so we don't see any effects of paging. 
    m.lock(); 
    m.unlock(); 
    
    QueryPerformanceCounter(&start); 
    for (int i = 0; i < 1000000; i++) 
    { 
        m.lock(); 
        m.unlock(); 
    } 
    
    QueryPerformanceCounter(&end); 
    
    int totalTimeM = (int)((end.QuadPart - start.QuadPart) * 1000/freq.QuadPart); 
    
    
    printf("C++ Mutex: %d Mutex: %d CritSec: %d\n", totalTimeM, totalTime, totalTimeCS); 
    

    私の結果は、最後の2のための時間の私の比率はマイケルのとほぼ同等であるが、私のマシンは彼よりも少なくとも4歳年下ですので、あなたが証拠を見ることができることに注意してください(217、473、および19でしたXPS-8700が出たときの2009年から2013年の間の速度の増加)。新しいミューテックスクラスは、Windowsミューテックスの2倍の速さですが、Windows CRITICAL_SECTIONオブジェクトの速度の10分の1未満です。私は非再帰的なmutexをテストしたことに注意してください。 CRITICAL_SECTIONオブジェクトは再帰的です(1回のスレッドは、同じ回数のままであれば、それらを繰り返し入力できます)。

    関連する問題