回答:
ロックは相互排除に使用されます。コードの一部がアトミックであることを確認したい場合は、コードをロックします。理論的にはバイナリセマフォを使用してこれを行うことができますが、それは特別なケースです。
セマフォと条件変数は、ロックによって提供される相互排除の上に構築され、共有リソースへの同期アクセスを提供するために使用されます。同様の目的で使用できます。
条件変数は、リソースが利用可能になるのを待つ間、ビジーな待機(条件を確認しながら繰り返しループする)を回避するために通常使用されます。たとえば、キューが空になるまで先に進むことができないスレッド(または複数のスレッド)がある場合、ビジー待機アプローチは次のようにするだけです。
//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つだけで、それは共有リソースです。マシンの在庫を維持する責任があるベンダー(プロデューサー)である1つのスレッドと、マシンからソーダを取り出したいバイヤー(コンシューマー)であるNのスレッドがあります。マシン内のソーダの数は、セマフォを駆動する整数値です。
ソーダマシンに到達するすべてのバイヤー(消費者)スレッドは、セマフォdown()
メソッドを呼び出してソーダを取得します。これにより、マシンからソーダが取得され、使用可能なソーダの数が1つ減ります。使用可能なソーダがある場合、コードはdown()
問題なくステートメントを通過し続けます。ソーダが使用できない場合、スレッドはここでスリープ状態になり、ソーダが再び使用可能になったとき(マシンにソーダがさらにあるとき)の通知を待ちます。
ベンダー(プロデューサー)スレッドは基本的に、ソーダマシンが空になるのを待っています。ベンダーから最後のソーダがマシンから取り出されると通知されます(1人以上の消費者がソーダの取り出しを待っている可能性があります)。ベンダーは、セマフォup()
メソッドを使用してソーダマシンを補充し、使用可能なソーダの数を毎回インクリメントして、待機中のコンシューマスレッドに、より多くのソーダが使用可能であることを通知します。
同期変数のwait()
およびsignal()
メソッドは、セマフォのdown()
およびup()
操作内に隠される傾向があります。
確かに、2つの選択肢の間には重複があります。セマフォまたは条件変数(または条件変数のセット)の両方があなたの目的に役立つことができる多くのシナリオがあります。セマフォと条件変数はどちらも、相互排除を維持するために使用するロックオブジェクトに関連付けられていますが、スレッドの実行を同期するためにロックの上に追加の機能を提供します。どれがあなたの状況に最も意味があるかを見つけるのは、主にあなた次第です。
それは必ずしも最も技術的な説明ではありませんが、それが私の頭の中で理にかなっている方法です。
内部の内容を明らかにしましょう。
条件付き変数は基本的に待機キューであり、ブロッキング待機操作とウェイクアップ操作をサポートします。つまり、スレッドを待機キューに入れてその状態をBLOCKに設定し、そこからスレッドを取得してその状態をREADYに設定できます。
条件変数を使用するには、他に2つの要素が必要です。
プロトコルはその後、
セマフォは基本的に、カウンター+ミューテックス+待機キューです。また、外部依存なしでそのまま使用できます。ミューテックスまたは条件変数として使用できます。
したがって、セマフォは条件付き変数よりも洗練された構造として扱うことができますが、後者はより軽量で柔軟です。
セマフォと条件変数は非常によく似ており、ほとんど同じ目的で使用されます。ただし、1つを望ましいものにすることができる小さな違いがあります。たとえば、バリア同期を実装するためにセマフォを使用することはできませんが、条件変数が理想的です。
バリア同期とは、すべてのスレッドが、全員がスレッド関数の特定の部分に到達するまで待機する場合です。これは、バリアに到達したときに各スレッドによってデクリメントされた合計スレッドの値である静的変数を持つことによって実装できます。これは、最後のスレッドが到着するまで各スレッドをスリープさせたいことを意味します。セマフォはまったく逆のことを行います!セマフォを使用すると、各スレッドは実行を継続し、最後のスレッド(セマフォの値を0に設定します)はスリープ状態になります。
一方、条件変数は理想的です。各スレッドがバリアに到達すると、静的カウンターがゼロかどうかを確認します。そうでない場合は、条件変数の待機関数を使用してスレッドをスリープ状態に設定します。最後のスレッドがバリアに到達すると、カウンター値はゼロにデクリメントされ、この最後のスレッドは他のすべてのスレッドを起動する条件変数シグナル関数を呼び出します。
モニターの同期の下で条件変数をファイルします。私は一般的にセマフォとモニターを2つの異なる同期スタイルとして見ました。本質的に保持される状態データの量と、コードをモデル化する方法の点で、2つの間に違いがあります。しかし、実際には片方で解決できるが、もう片方では解決できない問題はありません。
私はモニターフォームに向けてコーディングする傾向があります。私が作業するほとんどの言語では、ミューテックス、条件変数、およびいくつかのバッキング状態変数が使用されます。しかし、セマフォも機能します。
mutex
とconditional variables
から継承されますsemaphore
。
mutex
、semaphore
は2つの状態を使用します:0、1condition variables
semaphore
彼らは構文糖のようなものです