セマフォを使用する場合と条件変数を使用する場合
7 に答える
ロックは相互排除に使用されます。コードの一部がアトミックであることを確認したい場合は、その周りにロックをかけます。理論的にはバイナリセマフォを使用してこれを行うことができますが、これは特殊なケースです。
セマフォと条件変数は、ロックによって提供される相互排除の上に構築され、共有リソースへの同期アクセスを提供するために使用されます。それらは同様の目的に使用できます。
条件変数は通常、リソースが使用可能になるのを待機している間、ビジー待機(条件のチェック中に繰り返しループする)を回避するために使用されます。たとえば、キューが空になるまで続行できないスレッド(または複数のスレッド)がある場合、ビジーウェイトアプローチは次のようなことを行うことです。
//pseudocode
while(!queue.empty())
{
sleep(1);
}
これに伴う問題は、このスレッドに状態を繰り返しチェックさせることによってプロセッサ時間を浪費していることです。代わりに、リソースが使用可能であることをスレッドに通知するためにシグナルを送信できる同期変数を用意しないのはなぜですか?
//pseudocode
syncVar.lock.acquire();
while(!queue.empty())
{
syncVar.wait();
}
//do stuff with queue
syncVar.lock.release();
おそらく、キューから物事を引き出しているスレッドがどこかにあるでしょう。キューが空になると、syncVar.signal()
スリープ状態になっているランダムなスレッドをウェイクアップするために呼び出すことができますsyncVar.wait()
(または、通常、待機しているすべてのスレッドをウェイクアップするsignalAll()
orメソッドもあります)。broadcast()
私は通常、1つ以上のスレッドが単一の特定の条件で待機している場合(たとえば、キューが空になるため)、このような同期変数を使用します。
セマフォも同様に使用できますが、整数の利用可能なものに基づいて利用可能および利用不可能な共有リソースがある場合に、セマフォを使用する方が適切だと思います。セマフォは、生産者がリソースを割り当て、消費者がそれらを消費している生産者/消費者の状況に適しています。
あなたがソーダの自動販売機を持っていたかどうか考えてみてください。ソーダマシンは1台だけで、共有リソースです。マシンの在庫を維持する責任があるベンダー(プロデューサー)である1つのスレッドと、マシンからソーダを取り出したいバイヤー(コンシューマー)であるNスレッドがあります。マシン内のソーダの数は、セマフォを駆動する整数値です。
ソーダマシンに来るすべてのバイヤー(コンシューマー)スレッドは、セマフォdown()
メソッドを呼び出してソーダを取得します。これにより、マシンからソーダが取得され、使用可能なソーダの数が1つ減ります。使用可能なソーダがある場合、コードはdown()
問題なくステートメントを超えて実行され続けます。利用可能なソーダがない場合、スレッドはここでスリープし、ソーダが再び利用可能になったとき(マシンにさらにソーダがあるとき)に通知されるのを待ちます。
ベンダー(プロデューサー)スレッドは、基本的にソーダマシンが空になるのを待っています。ベンダーは、最後のソーダがマシンから取り出されたときに通知を受け取ります(そして、1人以上の消費者がソーダの取り出しを待っている可能性があります)。ベンダーはセマフォup()
方式でソーダマシンを補充し、利用可能なソーダの数は毎回増加し、それによって待機中の消費者スレッドはより多くのソーダが利用可能であることが通知されます。
同期変数のwait()
andメソッドは、セマフォのand操作signal()
内に隠される傾向があります。down()
up()
確かに、2つの選択肢の間には重複があります。セマフォまたは条件変数(または条件変数のセット)の両方が目的に役立つ可能性があるシナリオは数多くあります。セマフォと条件変数はどちらも、相互排除を維持するために使用するロックオブジェクトに関連付けられていますが、スレッドの実行を同期するために、ロックに加えて追加の機能を提供します。どちらがあなたの状況に最も適しているかを理解するのは、主にあなた次第です。
それは必ずしも最も技術的な説明ではありませんが、それが私の頭の中で理にかなっている方法です。
内部にあるものを明らかにしましょう。
条件変数は基本的に待機キューであり、ブロッキング待機およびウェイクアップ操作をサポートします。つまり、スレッドを待機キューに入れてその状態をBLOCKに設定し、スレッドをそこから取り出してその状態をREADYに設定できます。
条件変数を使用するには、他に2つの要素が必要であることに注意してください。
- 条件(通常、フラグまたはカウンターをチェックすることによって実装されます)
- 状態を保護するミューテックス
プロトコルは次のようになります。
- ミューテックスを取得
- 状態を確認する
- 条件がtrueの場合はミューテックスをブロックして解放し、そうでない場合はミューテックスを解放します
セマフォは基本的に、カウンター+ミューテックス+待機キューです。また、外部に依存せずにそのまま使用できます。ミューテックスまたは条件変数として使用できます。
したがって、セマフォは条件付き変数よりも洗練された構造として扱うことができますが、条件付き変数はより軽量で柔軟性があります。
セマフォは、変数への排他的アクセスを実装するために使用できますが、同期に使用することを目的としています。一方、ミューテックスには、相互排除に厳密に関連するセマンティクスがあります。リソースをロックしたプロセスのみが、リソースのロックを解除できます。
残念ながら、ミューテックスとの同期を実装することはできません。そのため、条件変数があります。また、条件変数を使用すると、ブロードキャストロック解除を使用して、待機中のすべてのスレッドを同時にロック解除できることに注意してください。これは、セマフォでは実行できません。
セマフォ変数と条件変数は非常に似ており、ほとんど同じ目的で使用されます。ただし、1つを望ましいものにする可能性のある小さな違いがあります。たとえば、バリア同期を実装するには、セマフォを使用できませんが、条件変数が理想的です。
バリア同期とは、スレッド関数の特定の部分に全員が到着するまで、すべてのスレッドを待機させる場合です。これは、静的変数を使用することで実装できます。静的変数は、最初は各スレッドがそのバリアに到達したときに減少する合計スレッドの値です。これは、最後のスレッドが到着するまで各スレッドをスリープ状態にすることを意味します。セマフォは正反対のことをします。セマフォを使用すると、各スレッドは実行を継続し、最後のスレッド(セマフォ値を0に設定します)がスリープ状態になります。
一方、条件変数は理想的です。各スレッドがバリアに到達すると、静的カウンターがゼロかどうかを確認します。そうでない場合は、条件変数wait関数を使用してスレッドをスリープ状態に設定します。最後のスレッドがバリアに到達すると、カウンタ値がゼロにデクリメントされ、この最後のスレッドが条件変数信号関数を呼び出して、他のすべてのスレッドをウェイクアップします。
モニター同期の下で条件変数をファイルします。私は一般的に、セマフォとモニターを2つの異なる同期スタイルとして見てきました。本質的に保持される状態データの量とコードのモデル化方法に関しては、2つの間に違いがありますが、実際には、一方では解決できるが他方では解決できない問題はありません。
私はモニターフォームに向けてコーディングする傾向があります。私が働いているほとんどの言語では、ミューテックス、条件変数、およびいくつかのバッキング状態変数に行き着きます。しかし、セマフォもその役割を果たします。
mutex
とはconditional variables
から継承されsemaphore
ます。
- の場合
mutex
、semaphore
は2つの状態を使用します:0、1 - 使用カウンター用
condition variables
。semaphore
それらはシンタックスシュガーのようなものです
conditionVar +mutex==セマフォ