ロック、ミューテックス、セマフォ…違いは何ですか?


回答:


533

ロックは、1つのスレッドだけがロックされた部分に入ることを許可し、ロックは他のプロセスと共有されません。

mutexはロックと同じですが、システム全体(複数のプロセスで共有)にすることができます。

セマフォは、ミューテックスと同じことが、スレッドのx個が入力することができ、これは、CPUの数を制限するIOまたは同時に実行集中タスクをRAMへ例のために使用することができます。

mutexとセマフォの違いに関する詳細な投稿については、こちらをご覧ください

また、任意の時点で無制限のリーダーまたは1つのライターを許可する読み取り/書き込みロックもあります。


2
@mertinan聞いたことはありませんが、ウィキペディアで「ラッチ(データベース)、(比較的短命の)インデックスのようなシステムデータ構造のロック」と書かれています
Peter

2
モニターは、特定の条件(ロックが解除されたときなど)を待つことができます。
Dzmitry Lazerka 2013年

25
セマフォはミューテックスとは異なります。それらは非常に異なって使用され、また異なるプロパティ(つまり所有権に関する)を持っています。詳細については、たとえば、barrgroup.com / Embedded
Systems / How

3
@nanoquackは、誤解を招く、または間違っていると思われる場合は、自由に編集してください。
ピーター

3
ミューテックスとセマフォの明確な区別については、nanoquackのリンクで、重要なパラグラフは「セマフォの正しい使用は、あるタスクから別のタスクへのシグナリング用です。ミューテックスは、常にこの順序で取得され、解放されます。保護する共有リソースを使用するタスク。対照的に、セマフォを使用するタスクはシグナルまたは待機のどちらかではなく両方を送信します。 "
ToolmakerSteve

117

これらの言葉に関しては多くの誤解があります。

これは以前の投稿(https://stackoverflow.com/a/24582076/3163691)からのものです。

1)クリティカルセクション = 1つのプロセス内で他の多くのスレッドから1つのアクティブスレッドのみを実行できるようにするために使用されるユーザーオブジェクト。他の選択されていないスレッド(@このオブジェクトを取得)は、スリープ状態になります。

[プロセス間機能なし、非常にプリミティブなオブジェクト]。

2)ミューテックスセマフォ(別名ミューテックス) = さまざまなプロセス間で、他の多くのスレッドから1つのアクティブスレッドのみを実行できるようにするために使用されるカーネルオブジェクト。他の選択されていないスレッド(@このオブジェクトを取得)は、スリープ状態になります。このオブジェクトは、スレッドの所有権、スレッドの終了通知、再帰(同じスレッドからの複数の「取得」呼び出し)、および「優先順位の逆転回避」をサポートします。

[プロセス間機能、非常に安全に使用できる、一種の「高レベル」同期オブジェクト]。

3)セマフォ(別名セマフォ)をカウントする = 他の多くのアクティブスレッドのグループの実行を許可するために使用されるカーネルオブジェクト。他の選択されていないスレッド(@このオブジェクトを取得)は、スリープ状態になります。

[ただし、次の 'mutex'属性が欠けているため、プロセス間機能の使用は安全ではありません:スレッド終了通知、再帰?、 '優先順位逆転回避?'など]。

4)そして今、「スピンロック」について話します、最初にいくつかの定義:

クリティカル領域= 2つ以上のプロセスで共有されるメモリの領域。

Lock =値が「クリティカルリージョン」への入り口を許可または拒否する変数。(単純な「ブールフラグ」として実装できます)。

ビジー待機=ある値が現れるまで、変数を継続的にテストします。

最終的に:

スピンロック(別名スピンロック) = ビジー待機を使用するロック。(ロックの取得は、xchgまたは同様のアトミック操作によって行われます)。

[スレッドスリープなし。主にカーネルレベルでのみ使用されます。ユーザーレベルのコードには非効率的です。

最後のコメントとしてはわかりませんが、上記の最初の3つの同期オブジェクト(#1、#2、および#3)がこの単純な獣(#4)を実装の一部として利用していることには、大きな見返りがあります。

良い一日を過ごしてください!。

参照:

-Qing LiとCaroline Yaoによる組み込みシステムのリアルタイムコンセプト(CMP Books)。

-Andrew Tanenbaum(Pearson Education International)による最新のオペレーティングシステム(3番目)。

-Jeffrey Richter(Microsoftプログラミングシリーズ)によるMicrosoft Windows用プログラミングアプリケーション(第4回)。

また、https//stackoverflow.com/a/24586803/3163691もご覧ください。


1
実際には、クリティカルセクションはカーネルオブジェクトではないため、より軽量で、プロセス間で同期することができません。
Vladislavs Burakovs、2015年

2
@ウラジスラフスブラコフス:あなたは正しいです!私の編集を許してください。一貫性を保つために修正します。
ファンテ

mutexとセマフォの明確な区別については、nanoquackが他の場所で言及しているように、barrgroup.com / Embedded - Systems / How - To / RTOS - Mutex - Semaphore-重要なパラグラフは「セマフォの正しい使用は、1つのタスクからのシグナリング用ですミューテックスは、保護されている共有リソースを使用する各タスクによって、常にこの順序で取得および解放されることを意味します。対照的に、セマフォを使用するタスクは、シグナルまたは待機のどちらかではなく両方を送信します。 "
ToolmakerSteve

[非効率的な]スピンロックに基づいて構築された他のロックメカニズムを推測します。AFAIKには、いくつかのアトミック操作とスリープキューのみが必要です。スピンロックがどこまでもされ、カーネル内に必要で説明したように、近代的な解決策は、その影響を最小限に抑えるスピンロック- -代替ウィキペディア - 「適応ミューテックス『..と呼ばれるハイブリッドアプローチを使用する』という考えは、リソースがでロックされたアクセスしようとするとスピンロックを使用することです。現在実行中のスレッドですが、スレッドが現在実行されていない場合はスリープします(後者はシングルプロセッサシステムでは常に
当てはまり

@ToolmakerSteve、スレッドIDを「スリープキュー」に「挿入」しようとする際の「衝突」の問題に対して、「スピンロック」なしで「解決策」を提供してください。とにかく、ウィキペディアのテキストでは、実装でスピンロックが使用されていると結論付けています!!!。
ファンテ

27

ほとんどの問題は、(i)ロックのみ、(ii)セマフォのみ、...、または(iii)両方の組み合わせを使用して解決できます。お気づきかもしれませんが、どちらも非常に似ています。どちらも競合状態を防ぎ、どちらもacquire()/ release()オペレーションを実行し、どちらも0個以上のスレッドをブロック/疑わしくします...実際、重大な違いはそれらどのようにロックおよびロック解除されるにのみあります。

  • ロック(またはミューテックスは)二つの状態(0又は1)を有します。これは、どちらかの可能なアンロックまたはロックされました。これらは、一度に1つのスレッドのみがクリティカルセクションに入るようにするためによく使用されます。
  • セマフォは、多くの州を持っている(0、1、2、...)。それすることができるロック(状態0)またはロック解除(状態1、2、3、...)。1つ以上のセマフォが一緒に使用されることが多く、あるリソースのユニット数が特定の値に達した/達していない場合に、1つのスレッドだけがクリティカルセクションに入るようにします(その値までカウントダウンするか、その値までカウントアップします)。 )。

両方のロック/セマフォでacquire()、プリミティブが状態0のときに呼び出しを試みると、呼び出しスレッドが中断されます。ロックの場合-ロックを取得する試みは状態1で成功します。セマフォの場合-状態{1、2、3、...}でロックを取得する試みは成功します。

状態が0のロックの場合、以前にを呼び出していたのと同じスレッドacquire()がreleaseを呼び出すと、解放は成功します。別のスレッドがこれを試みた場合-何が起こるか(通常、その試みは無視されるか、エラーがスローされます)は、実装/ライブラリーに依存します。状態0のセマフォの場合、任意のスレッドが解放を呼び出すことができ、解放は成功します(以前にどのスレッドが獲得を使用してセマフォを状態0にしたかに関係なく)。

前述の説明から、ロックには所有者の概念(リリースを呼び出すことができる唯一のスレッドは所有者)があることがわかりますが、セマフォには所有者がありません(どのスレッドもセマフォでリリースを呼び出すことができます)。


多くの混乱を引き起こすのは、実際にはこれらはこの高レベルの定義の多くのバリエーションであることです。

考慮すべき重要なバリエーション

  • acquire()/は何release()と呼ばれるべきですか?-[ 大幅に異なります]
  • ロック/セマフォは「キュー」または「セット」を使用して、待機中のスレッドを記憶していますか?
  • ロック/セマフォを他のプロセスのスレッドと共有できますか?
  • ロックは「再入可能」ですか?-[通常はい]。
  • ロックは「ブロック/非ブロック」ですか?-[通常、非ブロッキングはブロッキングロック(別名、スピンロック)がビジー待機を引き起こすために使用されます]。
  • 操作が「アトミック」であることをどのように確認しますか?

これらはあなたの本/講師/言語/ライブラリ/環境に依存します。
いくつかの言語がこれらの詳細にどのように答えるかに注意するクイックツアーがあります。


C、C ++(pthreads

  • ミューテックスを経由して実装されていますpthread_mutex_t。デフォルトでは、他のプロセス(PTHREAD_PROCESS_PRIVATE)と共有できませんが、ミューテックスにはpsharedと呼ばれる属性があります。設定すると、ミューテックスはプロセス間で共有されます(PTHREAD_PROCESS_SHARED)。
  • ロックは、ミューテックスと同じものです。
  • セマフォを経由して実装されていますsem_t。ミューテックスと同様に、セマフォは多数のプロセスのスレッド間で共有したり、1つの単一プロセスのスレッドに対してプライベートに保つことができます。これは、に提供されるpshared引数に依存しsem_initます。

python(threading.py

  • ロックthreading.RLock)主にC / C ++と同じであるpthread_mutex_tS。どちらも再入可能です。これは、それらをロックしたのと同じスレッドによってのみロック解除できることを意味します。これは、sem_tセマフォ、threading.Semaphoreセマフォ、およびtheading.Lockロックが再入可能でない場合です。これは、任意のスレッドがロックのロック解除/セマフォのダウンを実行できる場合です。
  • ミューテックスは、ロック(この用語はPythonでしばしば使用されていない)と同じです。
  • セマフォはthreading.Semaphore)ほとんど同じですsem_t。をsem_t使用しても、スレッドIDのキューは、ロックされているスレッドをロックしようとしたときにスレッドがブロックされた順序を記憶するために使用されます。スレッドがセマフォのロックを解除すると、キュー内の最初のスレッド(存在する場合)が新しい所有者として選択されます。スレッド識別子がキューから削除され、セマフォが再びロックされます。ただし、threading.Semaphoreでは、キューの代わりにセットが使用されるため、スレッドがブロックされた順序は保存されません。セット内のどのスレッドも次の所有者として選択される場合があります。

Java(java.util.concurrent

  • ロックjava.util.concurrent.ReentrantLock)主にC / C ++と同じであるpthread_mutex_tの、そしてPythonのthreading.RLockそれはまた、リエントラントロックを実現することができます。プロセス間でのロックの共有は、JVMが仲介者として機能するため、Javaでは困難です。スレッドが所有していないロックを解除しようとすると、IllegalMonitorStateExceptionがスローされます。
  • ミューテックスは、ロック(この用語はJavaでしばしば使用されていない)と同じです。
  • セマフォはjava.util.concurrent.Semaphore)ほとんど同じであるsem_tthreading.Semaphore。Javaセマフォのコンストラクタは、待機中のスレッドを格納するためにセット(false)とキュー(true)のどちらを使用するかを制御する公平性ブールパラメータを受け入れます。

理論的にはセマフォがよく議論されますが、実際にはセマフォはあまり使われていません。セマフォは1つの整数の状態のみを保持するため、多くの場合、柔軟性に欠け、一度に多くが必要になるため、コードの理解が困難になります。また、どのスレッドもセマフォを解放できるという事実は望ましくない場合があります。代わりに、よりオブジェクト指向の/より高いレベルの同期プリミティブ/「条件変数」や「モニター」などの抽象化が使用されます。


22

見てみましょうマルチスレッドのチュートリアルジョンKopplinによると。

スレッド間の同期」セクションでは、イベント、ロック、ミューテックス、セマフォ、待機可能タイマーの違いについて説明しています

ミューテックスは、共有リソースへの相互排他的アクセスを調整するためのスレッドを可能に、一度に一つのスレッドが所有することができます

クリティカルセクションオブジェクトは、ミューテックスオブジェクトによって提供されるものと同様の同期を提供しますが、クリティカルセクションオブジェクトは単一プロセスのスレッドによってのみ使用できます。

ミューテックスクリティカルセクションのもう1つの違いは、クリティカルセクションオブジェクトが現在別のスレッドによって所有されている場合、EnterCriticalSection()所有権を無期限に 待機するのに対し WaitForSingleObject()、ミューテックスで使用されるはタイムアウトを指定できることです。

セマフォは、同時に共有リソースにアクセスしているスレッドの数を制限し、ゼロといくつかの最大値との間のカウントを維持します。


15

私はそれを例でカバーしようとします:

ロック:使用lockする1つの例は、アイテム(一意のキーが必要)が追加される共有ディクショナリです。
ロックにより、別のスレッド(クリティカルセクションにある)が既にこのチェックに合格し、アイテムを追加している間に、ディクショナリにあるアイテムをチェックするコードのメカニズムに1つのスレッドが入りません。別のスレッドがロックされたコードを入力しようとすると、オブジェクトが解放されるまで待機(ブロック)されます。

private static readonly Object obj = new Object();

lock (obj) //after object is locked no thread can come in and insert item into dictionary on a different thread right before other thread passed the check...
{
    if (!sharedDict.ContainsKey(key))
    {
        sharedDict.Add(item);
    }
}

セマフォ: 接続のプールがあるとしましょう。セマフォが接続を取得するのを待つことで、単一のスレッドがプール内の1つの要素を予約する場合があります。次に接続を使用し、作業が完了するとセマフォを解放して接続を解放します。

私が大好きなコード例は、@ Patricによって与えられたバウンサーの1つです -ここでそれは行きます:

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);
        }
    }
}

ミューテックスそれはかなりSemaphore(1,1)広く、しばしばグローバルに使用されます(アプリケーション全体でそうでない場合は間違いなくlockより適切です)。Mutexグローバルにアクセス可能なリストからノードを削除するときは、globalを使用します(ノードを削除している間、最後に別のスレッドに何かを実行してもらいたい)あなたが取得するとMutex別のスレッドの試行が同じを取得する場合Mutex、それは、取得した同じスレッドまでスリープ状態に置かれるMutexのリリースにそれを。

グローバルミューテックスを作成する良い例は@deepeeです

class SingleGlobalInstance : IDisposable
{
    public bool hasHandle = false;
    Mutex mutex;

    private void InitMutex()
    {
        string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
        string mutexId = string.Format("Global\\{{{0}}}", appGuid);
        mutex = new Mutex(false, mutexId);

        var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
        var securitySettings = new MutexSecurity();
        securitySettings.AddAccessRule(allowEveryoneRule);
        mutex.SetAccessControl(securitySettings);
    }

    public SingleGlobalInstance(int timeOut)
    {
        InitMutex();
        try
        {
            if(timeOut < 0)
                hasHandle = mutex.WaitOne(Timeout.Infinite, false);
            else
                hasHandle = mutex.WaitOne(timeOut, false);

            if (hasHandle == false)
                throw new TimeoutException("Timeout waiting for exclusive access on SingleInstance");
        }
        catch (AbandonedMutexException)
        {
            hasHandle = true;
        }
    }


    public void Dispose()
    {
        if (mutex != null)
        {
            if (hasHandle)
                mutex.ReleaseMutex();
            mutex.Dispose();
        }
    }
}

次に次のように使用します:

using (new SingleGlobalInstance(1000)) //1000ms timeout on global lock
{
    //Only 1 of these runs at a time
    GlobalNodeList.Remove(node)
}

これにより時間を節約できることを願っています。


8

ウィキペディアには、セマフォとミューテックスの違いに関するすばらしいセクションがあります

ミューテックスは基本的にバイナリセマフォと同じものであり、同じ基本実装を使用する場合があります。それらの違いは次のとおりです。

mutexには、mutexをロックしたプロセスである所有者の概念があります。mutexをロックしたプロセスだけがロックを解除できます。対照的に、セマフォには所有者という概念がありません。どのプロセスでもセマフォのロックを解除できます。

セマフォとは異なり、ミューテックスは優先順位の反転の安全性を提供します。mutexは現在の所有者を知っているため、より優先度の高いタスクがmutexで待機を開始するたびに、所有者の優先度を上げることができます。

ミューテックスは、ミューテックスを保持しているプロセスが誤って削除されないように、削除の安全性も提供します。セマフォはこれを提供しません。


5

私の理解では、ミューテックスは単一のプロセス内でのみ使用できますが、セマフォは複数のプロセス間および対応するスレッドのセット間で使用できるのに対し、その多くのスレッドで使用できます。

また、mutexはバイナリ(ロックまたはロック解除)ですが、セマフォにはカウントの概念、または複数のロックとロック解除要求のキューがあります。

誰かが私の説明を確認できますか?私はLinux、特にカーネル2.6.32を使用するRed Hat Enterprise Linux(RHEL)バージョン6のコンテキストで話しています。


3
さて...これは、異なるオペレーティングシステムで異なる場合がありますが、Windowsにミューテックスは、少なくとも.NETミューテックスオブジェクト複数のプロセスで使用することができます
ピーター

2
stackoverflow.com/questions/9389730/…「同じプロセス内または他のプロセス内のスレッドはミューテックスを共有できます。」したがって、mutexはプロセス固有のものであってはなりません。
Peter

3

LinuxバリアントのCプログラミングを例のベースケースとして使用する。

ロック:

•通常、ロックまたはアンロックされた、動作中の非常に単純な構成バイナリ

•スレッドの所有権、優先度、順序付けなどの概念はありません。

•通常は、スレッドがロックの可用性を継続的にチェックするスピンロック。

•通常、アトミック操作に依存します(例:テストと設定、比較とスワップ、フェッチと追加など)。

•通常、アトミック操作にはハードウェアサポートが必要です。

ファイルロック:

•通常、複数のプロセスを介してファイルへのアクセスを調整するために使用されます。

•複数のプロセスが読み取りロックを保持できますが、単一のプロセスが書き込みロックを保持している場合、他のプロセスは読み取りまたは書き込みロックを取得できません。

•例:flock、fcntlなど。

ミューテックス:

•Mutex関数呼び出しは通常、カーネル空間で機能し、システムコールを引き起こします。

•所有権の概念を使用します。mutexを現在保持しているスレッドだけがロックを解除できます。

•Mutexは再帰的ではありません(例外:PTHREAD_MUTEX_RECURSIVE)。

•通常、条件変数との関連付けで使用され、pthread_cond_signal、pthread_cond_waitなどの引数として渡されます。

•一部のUNIXシステムでは、すべてのシステムで強制されているわけではありませんが、複数のプロセスでmutexを使用できます。

セマフォ:

•これは、値がゼロを下回ることを許可されていないカーネルが維持する整数です。

•プロセスの同期に使用できます。

•セマフォの値は、1より大きい値に設定できます。この場合、値は通常、使用可能なリソースの数を示します。

•値が1および0に制限されているセマフォは、バイナリセマフォと呼ばれます。


0

Supporting ownershipmaximum number of processes share lockおよびはmaximum number of allowed processes/threads in critical section、一般的な名前がの並行オブジェクトの名前/タイプを決定する3つの主要な要素ですlock。これらの因子の値は2値(2つの状態)であるため、3 * 8の真実のような表にまとめることができます。

  • X(所有権をサポートしていますか?):いいえ(0)/はい(1)
  • Y(#共有プロセス):> 1(∞)/ 1
  • Z(CAの#processes / threads):> 1(∞)/ 1

  X   Y   Z          Name
 --- --- --- ------------------------
  0   ∞   ∞   Semaphore              
  0   ∞   1   Binary Semaphore       
  0   1   ∞   SemaphoreSlim          
  0   1   1   Binary SemaphoreSlim(?)
  1   ∞   ∞   Recursive-Mutex(?)     
  1   ∞   1   Mutex                  
  1   1   ∞   N/A(?)                 
  1   1   1   Lock/Monitor           

このテーブルを自由に編集または拡張してください。編集可能にするために、ASCIIテーブルとして投稿しました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.