セマフォとは何ですか?


349

セマフォは、マルチスレッドの問題を解決するために頻繁に使用されるプログラミング概念です。コミュニティへの私の質問:

セマフォとは何ですか?どのように使用しますか?


14
整数カウンタが指定された上限に達したかどうかに基づいた値を持つブールフラグ。マックスへの難読化!
Sam

回答:


399

セマフォをナイトクラブの用心棒と考えてください。一度にクラブに入会できる献身的な人数がいます。クラブが満員の場合、誰も入場できませんが、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);
        }
    }
}

6
ナイトクラブの警備員のようなものであれば、ゲストを順番に入れるはずですが、試してみるとランダムです。例えば。ゲスト39の前にゲスト40が最初に来ました。これを制御するために何かできることはありますか?
TNA

2
@TNA:はい、それはこの例で新しいスレッドが開始される方法に関係しており、実際には答えの範囲ではありません。
Patrik Svensson、2014年

5
バウンサーの類推は確かに壮大ですが、興味深いことにすでに使用されています: albahari.com/threading/part2.aspx#_Semaphore
Igor Brejc

セマフォは分散システムでどのような価値を提供しますか?
csandreas1

198

記事ミューテックスとセマフォDemystified by Michael Barrは、ミューテックスとセマフォの違いを説明し、それらを使用する必要がある場合と使用しない場合について説明しています。ここではいくつかの重要な段落を抜粋しました。

重要な点は、共有リソースを保護するためにミューテックスを使用する必要がある一方で、シグナリングにはセマフォを使用する必要があることです。一般に、共有リソースを保護するためにセマフォを使用したり、シグナリングのためにミューテックスを使用したりしないでください。たとえば、共有リソースを保護するためにセマフォを使用するという点で、警備員のアナロジーには問題があります。そのように使用できますが、バグの診断が困難になる可能性があります。

ミューテックスとセマフォの実装にはいくつかの類似点がありますが、常に異なる方法で使用する必要があります。

一番上にある質問に対する最も一般的な(それでも正しくない)答えは、ミューテックスとセマフォは非常に似ているということです。唯一の重要な違いは、セマフォが1よりも多くカウントできることです。ほぼすべてのエンジニアは、ミューテックスがコードの重要なセクション内で相互排除を確実にすることによって共有リソースを保護するために使用されるバイナリフラグであることを正しく理解しているようです。しかし、「カウントセマフォ」の使用方法を拡張するように求められると、ほとんどのエンジニア(自信の度合いのみが異なります)は、これらがいくつかの同等のリソースを保護するために使用されるという教科書の意見の一部を表現します。

...

この時点で、バスルームキーを共有リソース(バスルーム)を保護するという考え方を使用して、興味深いアナロジーが作成されます。ショップにバスルームが1つしかない場合、そのリソースを保護し、複数の人が同時に使用するのを防ぐには、1つのキーで十分です。

バスルームが複数ある場合、それらに同じようにキーを設定して複数のキーを作成したくなるかもしれません。これは、セマフォが誤って使用されていることに似ています。いったんキーを取得すると、どのバスルームが利用可能か実際にはわかりません。このパスをたどると、おそらくミューテックスを使用してその情報を提供し、すでに占有されているバスルームを使用しないようにします。 。

セマフォは本質的に同じリソースのいくつかを保護するための間違ったツールですが、これは多くの人々がそれを考えて使用する方法です。バウンサーのアナロジーは明らかに異なります。同じタイプのリソースはいくつかありませんが、複数の同時ユーザーを受け入れることができる1つのリソースがあります。セマフォはそのような状況で使用できると思いますが、類推が実際に当てはまる現実の状況はめったにありません-同じタイプのいくつかがまだありますが、使用できないバスルームなどの個別のリソースがまだありますこちらです。

...

セマフォの正しい使用法は、あるタスクから別のタスクへのシグナリング用です。mutexは、保護する共有リソースを使用する各タスクによって、常にこの順序で取得および解放されることを意図しています。対照的に、セマフォを使用するタスクは、シグナルまたは待機のいずれかを送信します。両方は送信しません。たとえば、タスク1には、「電源」ボタンが押されたときに特定のセマフォをポスト(つまり、シグナルまたはインクリメント)するコードが含まれ、タスク2はディスプレイをウェイクアップし、同じセマフォで待機します。このシナリオでは、1つのタスクはイベント信号のプロデューサーです。他の消費者。

...

ここで重要なポイントは、ミューテックスがリアルタイムのオペレーティングシステムに悪い方法で干渉し、リソースの共有のために重要度の低いタスクが重要度​​の高いタスクの前に実行される優先順位の逆転を引き起こすということです。つまり、これは、優先度の低いタスクがミューテックスを使用してリソースAを取得し、次にBを取得しようとしたが、Bが利用できないために一時停止した場合に発生します。待機中は、優先度の高いタスクが発生し、Aが必要になりますが、すでに拘束されており、Bを待機しているため実行されていないプロセスによっても発生しています。これを解決する方法はたくさんありますが、ほとんどの場合は修正されていますmutexとタスクマネージャを変更する。これらの場合、mutexはバイナリセマフォよりもはるかに複雑です。

...

ミューテックスとセマフォの間の現代の広範な混乱の原因は歴史的であり、それは1974年にジクストラによるセマフォ(この記事では大文字の「S」)の発明にまでさかのぼります。その日付以前は、コンピュータサイエンティストに知られている割り込みセーフタスク同期および信号メカニズムは、3つ以上のタスクで使用するために効率的に拡張できませんでした。ダイクストラの革新的で安全かつスケーラブルなセマフォは、クリティカルセクションの保護とシグナリングの両方に適用されました。そして混乱が始まった。

ただし、優先順位ベースのプリエンプティブRTOS(VRTXなど、1980年頃)の出現、RMAを確立する学術論文の公開、優先順位の逆転によって引き起こされる問題、および優先順位に関する論文の後に、オペレーティングシステムの開発者には後で明らかになりました。 1990年の継承プロトコル3では、ミューテックスはバイナリカウンターを備えたセマフォだけではないことが明らかになりました。

Mutex:リソース共有

セマフォ:シグナリング

副作用を注意深く考慮せずに、一方を他方に使用しないでください。


10
このスタンフォード並行性PDF文書を見てください。8ページを参照してください。上記の説明は、よりわかりやすく
Kris Subramanian

3
セマフォ小さな本は、これらの問題についての貴重な読み物です。
G.バッハ

@KrisSubramanianリンクをありがとう。しかし、このドキュメントではセマフォについて説明し、ミューテックスについては何も述べていません。ただし、例の共有バッファーは、Mutexを使用して保護できるということですか?2つのセマフォemptyBuffersとfullBuffersの代わりに
talekeDskobeDa

1
@Pramod True。リンクはミューテックス関連のメモを追加しません。リンクを追加して、セマフォの側面がSO読者に明確になるようにしました。:)興味深いことに、この場合、バッファは順次アクセスされ、循環形式でアクセスされるため、ロックなしで使用されています。つまり、ライターは0に書き込み、リーダーに0から読み取るように通知します。リーダーが0から読み取らず、ライターに信号を送信しない場合、ライターはブロックされます。したがって、共通リソースをロックするためにミューテックスを使用する必要はありません。これは、上記のバスルームのアナロジーとは異なります。
クリススブラマニアン

@Kris Subramanian:すばらしいドキュメントですが、小さな誤解が含まれています。3番目のページに、「セマフォをロックする各スレッドは、ロックを解除するように注意する必要がある」と記載されており、どのスレッドでもロック解除できます。同じスレッドで実行する場合は、「ブロッケンミューテックス」として使用するだけです。「ブロッケン」は、他のスレッドから意図せずにロックを解除できるため-間違いが発生し、ロジックが壊れます。まだいいドキュメントだと思った。
ロスタムA.

70

Mutex:リソースへの排他メンバーアクセス

セマフォ:リソースへのnメンバーのアクセス

つまり、ミューテックスを使用して、カウンター、ファイル、データベースなどへのアクセスを同期できます。

sempahoreも同じことができますが、一定数の同時発信者をサポートします。たとえば、データベースコールをsemaphore(3)でラップして、マルチスレッドアプリが最大3つの同時接続でデータベースにアクセスできるようにします。3つのスロットの1つが開くまで、すべての試行がブロックされます。彼らは素朴なスロットルをするようなことを本当に、本当に簡単にします。


20
0と1:リチャード・W. Stevens氏によると、mutexは2つだけの可能な値で、実際にはバイナリセマフォである
強徐

19
@QiangXuのWilliam Stallingsによるオペレーティングシステムの内部と設計原則では、バイナリセマフォは1つの非常に重要な点でミューテックスとは異なります。私は引用します。対照的に、1つのプロセスがバイナリセマフォをロックし、別のプロセスがそれをロック解除することが可能です。」
Electric Coffee

3
古いスレッドにコメントするリスクがあるため、これは正しくありません。@AdamDavisが前述したように、リソースへのnメンバーのアクセスにはセマフォを使用してはいけません(must?)。これは、Mutexを使用して行う必要があります。コーヒーショップのバスルームの例を考えてみましょう。複数の人がアクセスを待っている場合や、バスルームと同様のキーを持つ複数のバスルームがある場合を考えます。むしろ、セマフォはタスク間のシグナリングに使用する必要があります。
cspider

21

運転手も含めて3人()+2人()のタクシーが乗れるとしましょう。したがって、semaphore一度に許可されるのは、車内に5人だけです。とmutexは、車の1つの座席に1人のみ許可します。

したがって、Mutexはリソース(OSスレッドのような)の排他的アクセスSemaphoreを許可し、aは一度にn個のリソースのアクセスを許可します。


19

@クレイグ:

セマフォはリソースをロックする方法であり、コードの一部が実行されている間、このコードのみがそのリソースにアクセスできることが保証されます。これにより、2つのスレッドが同時にリソースにアクセスできなくなり、問題が発生する可能性があります。

これは1つのスレッドだけに制限されません。セマフォは、一定数のスレッドがリソースにアクセスできるように構成できます。


7
これは解説であり、答えではありません。
カスペルスキー2013年

11
ええ、でもスタックオーバーフローにコメントが追加される前に書いたと思います。または、覚えていません。今回もコメントで答えました。:-)
Mats Fredriksson

16

セマフォは...セマフォとしても使用できます。たとえば、キュ​​ーにデータをエンキューする複数のプロセスがあり、キューからのデータを消費するタスクが1つだけの場合です。使用中のタスクが常にキューをポーリングして利用可能なデータを取得したくない場合は、セマフォを使用できます。

ここでは、セマフォは除外メカニズムとしてではなく、シグナリングメカニズムとして使用されています。消費タスクはセマフォを待機しています。生成タスクはセマフォにポストしています。

このようにして、デキューされるデータがある場合にのみ、消費タスクが実行されます。


11

並行プログラムの構築には、同期と相互排除という2つの重要な概念があります。これらの2種類のロック(セマフォは、より一般的には一種のロックメカニズム)が同期と相互排他を実現するのにどのように役立つかを見ていきます。

セマフォは、同期と相互排除の両方を実装することにより、並行性の実現を支援するプログラミング構造です。セマフォには、バイナリとカウントという2つのタイプがあります。

セマフォには2つの部分があります。カウンタと、特定のリソースへのアクセスを待機しているタスクのリストです。セマフォは2つの操作を実行します。待機(P)[これはロックの取得に似ています]と解放(V)[ロックの解放と同様]-これらは、セマフォに対して実行できる2つの操作のみです。バイナリセマフォでは、カウンタは論理的に0と1の間になります。これは、オープン/クローズの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が最初に実行されたとしましょう-カウンターが0(クローズ)であるため、sem.P()に到達して待機します。スレッドBが到着し、B1を完了してから、スレッド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をどのように配置するかを検討してください)

(検索:Vの数がPの数より大きい場合はどうなりますか?)

カウントセマフォを使用した相互排除:このために独自の疑似コードを作成してください(理解しやすくなります)。ただし、基本的な概念は次のとおりです。counter= Nのカウントセマフォにより、N個のタスクがクリティカルセクションに自由に入ることができます。 。つまり、N個のタスク(または、必要に応じてスレッド)がクリティカルセクションに入りますが、N + 1番目のタスクはブロックされ(お気に入りのブロックされたタスクのリストに移動します)、誰かVがセマフォであるときにのみ許可されます。少なくとも一度は。そのため、セマフォカウンターは0と1の間で変動するのではなく、0とNの間で移動するようになり、N個のタスクが自由に出入りできるようになり、誰もブロックしません!

さて、どうしてそんなばかげたものが必要なのでしょうか。複数の人がリソースにアクセスできないようにする相互排除の全体のポイントではないですか?(ヒントヒント...コンピュータにドライブが1つしかないわけではありません...?)

考えてみよう:セマフォを数えるだけで相互排除は実現できるのか リソースのインスタンスが10個あり、10個のスレッドが(カウントセマフォを通じて)入って、最初のインスタンスを使用しようとした場合はどうなりますか?


7

セマフォは、2つの変更操作が定義されている自然数(つまり、ゼロ以上の整数)を含むオブジェクトです。1つの演算、V自然に1を追加します。他の操作はP、自然数を1減らします。両方のアクティビティはアトミックです(つまり、a Vまたはa と同時に他の操作を実行することはできませんP)。

自然数0を減らすことはできないため、P0を含むセマフォを呼び出すと、数が0でなくなりP正常に(そしてアトミックに)実行できるようになるまで、呼び出しプロセス(/スレッド)の実行がブロックされます。

他の回答で述べたように、セマフォを使用して、特定のリソースへのアクセスをプロセスの最大数(ただし可変)に制限できます。


7

アイデアを理解するのに役立つ視覚化を作成しました。セマフォは、マルチスレッド環境での共通リソースへのアクセスを制御します。 ここに画像の説明を入力してください

ExecutorService executor = Executors.newFixedThreadPool(7);

Semaphore semaphore = new Semaphore(4);

Runnable longRunningTask = () -> {
    boolean permit = false;
    try {
        permit = semaphore.tryAcquire(1, TimeUnit.SECONDS);
        if (permit) {
            System.out.println("Semaphore acquired");
            Thread.sleep(5);
        } else {
            System.out.println("Could not acquire semaphore");
        }
    } catch (InterruptedException e) {
        throw new IllegalStateException(e);
    } finally {
        if (permit) {
            semaphore.release();
        }
    }
};

// execute tasks
for (int j = 0; j < 10; j++) {
    executor.submit(longRunningTask);
}
executor.shutdown();

出力

Semaphore acquired
Semaphore acquired
Semaphore acquired
Semaphore acquired
Could not acquire semaphore
Could not acquire semaphore
Could not acquire semaphore

記事のサンプルコード


3

ハードウェアまたはソフトウェアのフラグ。マルチタスクシステムでは、セマフォは、共通リソースのステータスを示す値を持つ変数として存在します。リソースを必要とするプロセスは、セマフォをチェックしてリソースのステータスを判断し、次に進む方法を決定します。


2

セマフォはスレッドリミッタのように機能します。

例: 100スレッドのプールがあり、DB操作を実行したい場合。一度に100スレッドがDBにアクセスする場合、DBにロックの問題がある可能性があるため、一度に制限されたスレッドのみを許可するセマフォを使用できます。以下の例では、一度に1つのスレッドのみを許可します。スレッドはacquire()メソッドを呼び出すとアクセスを取得し、release()メソッドを呼び出した後、アクセスを解放して次のスレッドがアクセスを取得できるようにします。

    package practice;
    import java.util.concurrent.Semaphore;

    public class SemaphoreExample {
        public static void main(String[] args) {
            Semaphore s = new Semaphore(1);
            semaphoreTask s1 = new semaphoreTask(s);
            semaphoreTask s2 = new semaphoreTask(s);
            semaphoreTask s3 = new semaphoreTask(s);
            semaphoreTask s4 = new semaphoreTask(s);
            semaphoreTask s5 = new semaphoreTask(s);
            s1.start();
            s2.start();
            s3.start();
            s4.start();
            s5.start();
        }
    }

    class semaphoreTask extends Thread {
        Semaphore s;
        public semaphoreTask(Semaphore s) {
            this.s = s;
        }
        @Override
        public void run() {
            try {
                s.acquire();
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+" Going to perform some operation");
                s.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } 
    }

1

だから、誰もがトイレに行こうとしていると想像してください。残りのキーが足りない場合、その人は待つ必要があります。したがって、セマフォを、さまざまなプロセス(バスルームの常駐者)がアクセスを要求できるバスルーム(システムリソース)で使用できるキーのセットを表すと考えてください。

次に、2つのプロセスが同時にトイレに行こうとしているところを想像してください。これは良い状況ではなく、これを防ぐためにセマフォが使用されます。残念ながら、セマフォは自発的なメカニズムであり、プロセス(私たちの浴室の常連客)はそれを無視できます(つまり、キーがあったとしても、誰かがドアを開けるだけです)。

バイナリ/ミューテックスとセマフォのカウントにも違いがあります。

http://www.cs.columbia.edu/~jae/4118/lect/L05-ipc.htmlで講義ノートを確認してください


0

これは古い質問ですが、セマフォの最も興味深い用途の1つは読み取り/書き込みロックであり、明示的には言及されていません。

読み取り/書き込みロックは単純な方法で機能します。読み取り側の1つの許可と書き込み側のすべての許可を使用します。実際、ar / wロックの簡単な実装ですが、読み取り時にメタデータを変更する必要があり(実際には2回)、ボトルネックになる可能性があり、ミューテックスやロックよりもはるかに優れています。

もう1つの欠点は、セマフォが公平なものであるか、複数のリクエストで書き込みが許可されていない限り、ライター同士を簡単に起動できることです。

さらに読む


-3

セマフォはリソースをロックする方法であり、コードの一部が実行されている間、このコードのみがそのリソースにアクセスできることが保証されます。これにより、2つのスレッドが同時にリソースにアクセスできなくなり、問題が発生する可能性があります。


13
セマフォではなくミューテックスのように聞こえる
Sam
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.