セマフォは、マルチスレッドの問題を解決するために頻繁に使用されるプログラミングの概念です。コミュニティへの私の質問:
セマフォとは何ですか?また、どのように使用しますか?
セマフォは、マルチスレッドの問題を解決するために頻繁に使用されるプログラミングの概念です。コミュニティへの私の質問:
セマフォとは何ですか?また、どのように使用しますか?
セマフォをナイトクラブの用心棒と考えてください。一度にクラブに入ることができる専用の人数があります。クラブが満員の場合は誰も入場できませんが、1 人が退席するとすぐに別の人が入場する可能性があります。
これは、特定のリソースのコンシューマーの数を制限する方法にすぎません。たとえば、アプリケーション内のデータベースへの同時呼び出しの数を制限します。
これはC#での非常に教育的な例です:-)
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace TheNightclub
{
public class Program
{
public static Semaphore Bouncer { get; set; }
public static void Main(string[] args)
{
// Create the semaphore with 3 slots, where 3 are available.
Bouncer = new Semaphore(3, 3);
// Open the nightclub.
OpenNightclub();
}
public static void OpenNightclub()
{
for (int i = 1; i <= 50; i++)
{
// Let each guest enter on an own thread.
Thread thread = new Thread(new ParameterizedThreadStart(Guest));
thread.Start(i);
}
}
public static void Guest(object args)
{
// Wait to enter the nightclub (a semaphore to be released).
Console.WriteLine("Guest {0} is waiting to entering nightclub.", args);
Bouncer.WaitOne();
// Do some dancing.
Console.WriteLine("Guest {0} is doing some dancing.", args);
Thread.Sleep(500);
// Let one guest out (release one semaphore).
Console.WriteLine("Guest {0} is leaving the nightclub.", args);
Bouncer.Release(1);
}
}
}
Michael Barrによって謎解きされたミューテックスとセマフォの記事は、ミューテックスとセマフォが異なる理由と、それらを使用する必要がある場合と使用しない場合についての非常に短い紹介です。ここでいくつかの重要な段落を抜粋しました。
重要な点は、共有リソースを保護するためにミューテックスを使用する必要があり、シグナリングにはセマフォを使用する必要があるということです。通常、共有リソースを保護するためにセマフォを使用したり、シグナリングにミューテックスを使用したりしないでください。たとえば、共有リソースを保護するためにセマフォを使用するという点でのバウンサーのアナロジーには問題があります。そのように使用することはできますが、バグの診断が困難になる可能性があります。
ミューテックスとセマフォの実装にはいくつかの類似点がありますが、常に異なる方法で使用する必要があります。
一番上に提示された質問に対する最も一般的な(しかしそれでも正しくない)答えは、ミューテックスとセマフォが非常に似ているということです。唯一の重要な違いは、セマフォが1より大きくカウントされる可能性があることです。ほぼすべてのエンジニアは、ミューテックスがコードのクリティカルセクション内で相互排除を保証することによって共有リソースを保護するために使用されるバイナリフラグであることを正しく理解しているようです。しかし、「カウントセマフォ」の使用方法を拡張するように求められた場合、ほとんどのエンジニアは、自信の程度だけが異なりますが、これらはいくつかの同等のリソースを保護するために使用されるという教科書の意見の一部を表現しています。
..。
この時点で、共有リソース(バスルーム)を保護するためのバスルームキーのアイデアを使用して、興味深いアナロジーが作成されます。ショップにバスルームが1つしかない場合、そのリソースを保護し、複数の人が同時に使用するのを防ぐには、1つのキーで十分です。
複数のバスルームがある場合、それらに同じようにキーを設定して複数のキーを作成したくなるかもしれません。これは、セマフォが誤用されているのと似ています。キーを取得すると、実際にどのバスルームが利用可能かわからなくなります。このパスをたどると、ミューテックスを使用してその情報を提供し、すでに占有されているバスルームを使用しないようにすることになります。 。
セマフォは、本質的に同じリソースのいくつかを保護するための間違ったツールですが、これは多くの人々がそれを考えて使用する数です。バウンサーの例えは明らかに異なります。同じタイプのリソースがいくつかあるのではなく、複数の同時ユーザーを受け入れることができる1つのリソースがあります。セマフォはそのような状況で使用できると思いますが、アナロジーが実際に当てはまる現実の状況はめったにありません-同じタイプがいくつかあるが、バスルームなどの個別のリソースは使用できないことがよくありますこちらです。
..。
セマフォの正しい使用法は、あるタスクから別のタスクへのシグナリングです。ミューテックスは、保護する共有リソースを使用する各タスクによって、常にこの順序で取得および解放されることを目的としています。対照的に、セマフォを使用するタスクは、信号または待機のいずれかであり、両方ではありません。たとえば、タスク1には、「電源」ボタンが押されたときに特定のセマフォを送信(つまり、信号またはインクリメント)するコードが含まれ、タスク2は、同じセマフォを保留します。このシナリオでは、1つのタスクがイベント信号のプロデューサーです。他の消費者。
..。
ここで重要な点は、ミューテックスがリアルタイムオペレーティングシステムに悪い方法で干渉し、リソース共有のために重要度の低いタスクがより重要なタスクの前に実行される可能性がある優先順位の逆転を引き起こすことです。つまり、これは、優先度の低いタスクがミューテックスを使用してリソースAを取得し、次にBを取得しようとしたが、Bが使用できないために一時停止した場合に発生します。待機中に、優先度の高いタスクが発生し、Aが必要になりますが、すでに拘束されており、Bを待機しているために実行されていないプロセスによって発生します。これを解決する方法はたくさんありますが、ほとんどの場合、修正されます。ミューテックスとタスクマネージャーを変更する。これらの場合、ミューテックスはバイナリセマフォよりもはるかに複雑です。
..。
ミューテックスとセマフォの間の広範囲にわたる現代の混乱の原因は、Djikstraによる1974年のセマフォ(この記事では大文字の「S」)の発明にまでさかのぼる歴史的なものです。その日付以前は、コンピューター科学者に知られている割り込みセーフなタスク同期およびシグナリングメカニズムはいずれも、3つ以上のタスクで使用するために効率的にスケーラブルではありませんでした。ダイクストラの革新的で安全でスケーラブルなセマフォは、クリティカルセクションの保護とシグナリングの両方に適用されました。そして、混乱が始まりました。
しかし、優先度ベースのプリエンプティブRTOS(VRTX、1980年頃など)が登場し、RMAを確立する学術論文と優先順位の逆転によって引き起こされる問題が発表された後、オペレーティングシステムの開発者には、優先順位に関する論文が明らかになりました。 1990年の継承プロトコル3により、ミューテックスはバイナリカウンターを備えた単なるセマフォ以上のものでなければならないことが明らかになりました。
Mutex:リソース共有
セマフォ:シグナリング
副作用を慎重に考慮せずに一方を他方に使用しないでください。
Mutex:リソースへの排他的メンバーアクセス
セマフォ:リソースへのnメンバーアクセス
つまり、ミューテックスを使用して、カウンター、ファイル、データベースなどへのアクセスを同期させることができます。
sempahoreは同じことを行うことができますが、一定数の同時発信者をサポートします。たとえば、データベース呼び出しをセマフォ(3)でラップして、マルチスレッドアプリが最大3つの同時接続でデータベースにアクセスできるようにすることができます。3つのスロットのいずれかが開くまで、すべての試行がブロックされます。彼らは素朴なスロットルをするようなことを本当に、本当に簡単にします。
@クレイグ:
セマフォは、コードの一部が実行されている間、このコードの一部のみがそのリソースにアクセスできることが保証されるように、リソースをロックする方法です。これにより、2 つのスレッドが同時にリソースにアクセスすることがなくなり、問題が発生する可能性があります。
これは、1 つのスレッドだけに限定されません。セマフォは、一定数のスレッドがリソースにアクセスできるように構成できます。
セマフォは ... セマフォとしても使用できます。たとえば、キューにデータをエンキューするプロセスが複数あり、キューからデータを消費するタスクが 1 つだけの場合です。使用するタスクが利用可能なデータのキューを常にポーリングしないようにする場合は、セマフォを使用できます。
ここでは、セマフォは除外メカニズムとしてではなく、シグナリング メカニズムとして使用されます。消費側タスクはセマフォで待機しています。生成側タスクはセマフォにポストしています。
このようにして、デキューするデータがある場合にのみ、消費タスクが実行されます。
並行プログラムの構築には、同期と相互排除という 2 つの重要な概念があります。これら 2 種類のロック (より一般的には、セマフォは一種のロック メカニズムです) が同期と相互排除の実現にどのように役立つかを見ていきます。
セマフォは、同期と相互排除の両方を実装することにより、同時実行を実現するのに役立つプログラミング構造です。セマフォには、バイナリとカウンティングの 2 種類があります。
セマフォには、カウンターと、特定のリソースへのアクセスを待機しているタスクのリストの 2 つの部分があります。セマフォは 2 つの操作を実行します: 待機 (P) [これはロックの取得に似ています] と解放 (V) [ロックの解放に似ています] - これらは、セマフォに対して実行できる唯一の 2 つの操作です。バイナリ セマフォでは、カウンターは論理的に 0 と 1 の間を行き来します。これは、open/closed という 2 つの値を持つロックに似ていると考えることができます。カウンティング セマフォには、count の複数の値があります。
理解しておくべき重要なことは、セマフォ カウンターは、ブロックする必要のないタスクの数を追跡することです。つまり、タスクは進行できます。タスクはブロックされ、カウンタがゼロの場合にのみセマフォのリストに追加されます。したがって、タスクが進行できない場合、タスクは P() ルーチンのリストに追加され、V() ルーチンを使用して「解放」されます。
これで、バイナリ セマフォを使用して同期と相互排除を解決する方法が明らかになりました。これらは本質的にロックです。
元。同期:
thread A{
semaphore &s; //locks/semaphores are passed by reference! think about why this is so.
A(semaphore &s): s(s){} //constructor
foo(){
...
s.P();
;// some block of code B2
...
}
//thread B{
semaphore &s;
B(semaphore &s): s(s){} //constructor
foo(){
...
...
// some block of code B1
s.V();
..
}
main(){
semaphore s(0); // we start the semaphore at 0 (closed)
A a(s);
B b(s);
}
上記の例では、B2 は B1 の実行が終了した後にのみ実行できます。スレッド A が最初に実行され、sem.P() に到達し、カウンターが 0 (閉じている) であるため待機するとします。スレッド B が来て、B1 を終了し、次にスレッド A を解放し、スレッド A が B2 を完了します。したがって、同期を実現します。
次に、バイナリ セマフォを使用した相互排除を見てみましょう。
thread mutual_ex{
semaphore &s;
mutual_ex(semaphore &s): s(s){} //constructor
foo(){
...
s.P();
//critical section
s.V();
...
...
s.P();
//critical section
s.V();
...
}
main(){
semaphore s(1);
mutual_ex m1(s);
mutual_ex m2(s);
}
相互排除も非常に単純です。m1 と m2 は同時にクリティカル セクションに入ることができません。したがって、各スレッドは同じセマフォを使用して、2 つのクリティカル セクションの相互排除を提供します。では、同時実行性を高めることは可能でしょうか? クリティカル セクションによって異なります。(他にどのようにセマフォを使用して相互排除を実現できるか考えてみてください。ヒント ヒント: 必ずしも 1 つのセマフォのみを使用する必要がありますか?)
カウント セマフォ: 複数の値を持つセマフォ。これが何を意味するのか見てみましょう - 複数の値を持つロック?? オープン、クローズ、そして... うーん。相互排除または同期における多段ロックはどのような用途に使用されますか?
2 つのうち簡単な方を考えてみましょう。
カウント セマフォを使用した同期: 3 つのタスクがあるとします。#1 と 2 を 3 の後に実行したいとします。どのように同期を設計しますか?
thread t1{
...
s.P();
//block of code B1
thread t2{
...
s.P();
//block of code B2
thread t3{
...
//block of code B3
s.V();
s.V();
}
したがって、セマフォが閉じた状態で開始する場合、t1 および t2 ブロックがセマフォのリストに追加されるようにします。次に、すべての重要な t3 が来て、その仕事を終了し、t1 と t2 を解放します。解放される順番は?セマフォのリストの実装に依存します。FIFOである可能性があり、特定の優先度に基づいている可能性があります。(注: t1 と t2 を特定の順序で実行したい場合、およびセマフォの実装を認識していない場合は、P と V;s をどのように配置するかを考えてください)
(調べてください: V の数が P の数より多い場合はどうなりますか?)
カウント セマフォを使用した相互排除: このための独自の疑似コードを作成してもらいたい (理解が深まります!) - しかし、基本的な概念は次のとおりです。カウンタ = N のカウント セマフォでは、N 個のタスクがクリティカル セクションに自由に入ることができます。 . これが意味することは、N 個のタスク (または必要に応じてスレッド) がクリティカル セクションに入るが、N+1 番目のタスクがブロックされ (私たちのお気に入りのブロックされたタスク リストに入る)、V がセマフォである場合にのみ通過することを意味します。少なくとも一度は。そのため、セマフォ カウンターは、0 と 1 の間でスイングするのではなく、0 と N の間を移動し、N タスクが自由に出入りできるようになり、誰もブロックしません!
さて、どうしてそんなばかげたものが必要なのですか?複数の人がリソースにアクセスできないようにすることは、相互排除の全体的なポイントではありませんか?? (ヒント ヒント...コンピュータに常に 1 つのドライブしかないわけではありませんよね...?)
考えてみてください: カウンティング・セマフォだけで相互排他が実現されるのでしょうか? リソースの 10 個のインスタンスがあり、10 個のスレッドが (カウンティング セマフォを介して) 入ってきて、最初のインスタンスを使用しようとした場合はどうなるでしょうか?
セマフォは、2 つの変更操作が定義されている自然数 (ゼロ以上の整数) を含むオブジェクトです。1 つの演算 はV
、自然界に 1 を加算します。もう 1 つの操作 は、自然数を 1 減らします。どちらのアクティビティもアトミックです (つまり、 aまたは aP
と同時に他の操作を実行することはできません)。V
P
自然数の 0 は減らすことができないため、P
0 を含むセマフォを呼び出すと、数が 0 ではなくなり、P
正常に (かつアトミックに) 実行できるようになるまで、呼び出しプロセス (/スレッド) の実行がブロックされます。
他の回答で述べたように、セマフォを使用して、特定のリソースへのアクセスを最大(ただし可変)プロセス数に制限できます。
ハードウェアまたはソフトウェアのフラグ。マルチタスク システムでは、セマフォは、共通リソースのステータスを示す値を持つ変数です。リソースを必要とするプロセスは、セマフォをチェックしてリソースのステータスを判断し、処理方法を決定します。
誰もがトイレに行こうとしていて、トイレの鍵が一定数しかないと想像してみてください。十分なキーが残っていない場合、その人は待つ必要があります。したがって、セマフォは、さまざまなプロセス (バスルーム利用者) がアクセスを要求できる、バスルーム (システム リソース) で使用できる一連のキーを表すものと考えてください。
2 つのプロセスが同時にトイレに行こうとしているとします。これは良い状況ではなく、これを防ぐためにセマフォが使用されます。残念ながら、セマフォは任意のメカニズムであり、プロセス (私たちのトイレ利用者) はそれを無視できます (つまり、鍵があったとしても、誰かがドアを蹴って開けることができます)。
バイナリ/ミューテックスとカウンティングセマフォの間にも違いがあります。
http://www.cs.columbia.edu/~jae/4118/lect/L05-ipc.htmlで講義ノートを確認してください。
これは古い質問ですが、セマフォの最も興味深い用途の 1 つは読み取り/書き込みロックであり、明示的に言及されていません。
r/w ロックは単純な方法で機能します。つまり、リーダーに対して 1 つのパーミットを消費し、ライターに対してすべてのパーミットを消費します。実際、ar/w ロックの簡単な実装ですが、読み取り時に (実際には 2 回) メタデータを変更する必要があり、これがボトルネックになる可能性がありますが、ミューテックスやロックよりもはるかに優れています。
もう 1 つの欠点は、セマフォが公平なものであるか、書き込みが複数の要求で許可を取得しない限り、ライターもかなり簡単に開始できることです。
さらに読む:
セマフォは、コードの一部が実行されている間、このコードの一部のみがそのリソースにアクセスできることが保証されるように、リソースをロックする方法です。これにより、2 つのスレッドが同時にリソースにアクセスすることがなくなり、問題が発生する可能性があります。