web-dev-qa-db-ja.com

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

コンカレントプログラミングに関連してこれらの単語を聞いたことがありますが、それらの違いは何ですか?

353
victor

ロックすると、ロックされている部分に入ることができるスレッドは1つだけになり、ロックは他のプロセスと共有されません。

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

--- セマフォ はミューテックスと同じですが、x個のスレッドが入ることを許します。

ミューテックスとセマフォの違いについてのより詳細な投稿は ここ を読んでください。

また、いつでも無制限の数の読者または1人の作家を許可する読み取り/書き込みロックもあります。

443
Peter

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

これは以前の投稿( https://stackoverflow.com/a/24582076/3163691 )からのもので、ここに見事に適合しています:

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

[プロセス間機能なし、非常に原始的なオブジェクト]。

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

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

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

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

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

Critical Region = 2つ以上のプロセスによって共有されるメモリの領域。

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

ビジー待機=何らかの値が表示されるまで変数を継続的にテストします。

最後に:

スピンロック(別名スピンロック)= Alockビジー待機。 (lockの取得は、xchgまたは同様の原子操作)。

[スリープ状態のスレッドはありません。主にカーネルレベルでのみ使用されます。ユーザーレベルのコードにとっては非効率です。

最後のコメントとして、私は確信していませんが、上記の最初の3つの同期オブジェクト(#1、#2、#3)が実装の一部としてこの単純な獣(#4)を使用していることを大金を賭けます。

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

参照:

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

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

-Microsoft Windows用のプログラミングアプリケーション(4番目)、Jeffrey Richter(Microsoftプログラミングシリーズ)。

また、以下をご覧ください: https://stackoverflow.com/a/24586803/3163691

100
fante

John Kopplinによる Multithreading Tutorial を見てください。

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

Aミューテックスは一度に1つのスレッドしか所有できないため、スレッドは共有リソースへの相互排他的アクセスを調整できます。

クリティカルセクションオブジェクトは、ミューテックスオブジェクトと同様の同期を提供します。ただし、クリティカルセクションオブジェクトは単一プロセスのスレッドでしか使用できません。

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

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

20
onmyway133

ほとんどの問題は、(i)単なるロック、(ii)単なるセマフォ、...、または(iii)両方の組み合わせを使用して解決できます。おわかりのとおり、これらは非常に似ています。両方とも--- 競合状態 を防止し、両方ともacquire()/release()操作を行い、どちらも0個以上のスレッドをブロック/疑わしい状態にします how はロックとロック解除だけにあります。

  • A lock(またはmutex)には2つの状態(0または1)があります。 unlocked または locked のいずれかです。一度に1つのスレッドだけがクリティカルセクションに入るようにするためによく使用されます。
  • A セマフォは多くの状態(0、1、2、...)を持ちます。 ロック (状態0)または ロック解除 (状態1、2、3、...)になります。 1つ以上のセマフォが一緒に使用されて、あるリソースのユニット数が特定の値に達した/達していない場合に1つのスレッドだけがクリティカルセクションに入ることを保証します。 ).

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

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

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


多くの混乱を引き起こすのは、実際にはそれらがこの高水準の定義の多くの変形であるということです。

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

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

これらはあなたの本/講師/言語/図書館/環境によって異なります。
これは、いくつかの言語がどのようにこれらの詳細に答えるかを記した簡単なツアーです。


C、C++( pthreads

  • A --- [mutexpthread_mutex_tによって実装されています。デフォルトでは、他のプロセス(PTHREAD_PROCESS_PRIVATE)と共有することはできませんが、mutexには pshared という属性があります。設定すると、ミューテックスはプロセス間で共有されます(PTHREAD_PROCESS_SHARED)。
  • A lockはミューテックスと同じものです。
  • A semaphoresem_tによって実装されています。ミューテックスと同様に、セマフォは多くのプロセスの脅威間で共有したり、1つのプロセスのスレッドに対してプライベートにしたりすることができます。これはsem_initに与えられた pshared 引数に依存します。

python( threading.py

  • A lockthreading.RLock)はC/C++のpthread_mutex_tsとほとんど同じです。両方とも リエントラント です。つまり、ロックされているのと同じスレッドによってのみロック解除される可能性があります。 sem_tセマフォ、threading.Semaphoreセマフォ、そしてtheading.Lockロックは 再入可能ではありません - any スレッドはロック解除/セマフォダウンを実行できます。
  • A mutexはロックと同じです(この用語はPythonではあまり使われません)。
  • A semaphorethreading.Semaphore)はsem_tとほとんど同じです。 sem_tでは、スレッドIDのキューは、ロック中にロックしようとしたときにスレッドがブロックされた順番を記憶するために使用されます。スレッドがセマフォのロックを解除すると、キュー内の first スレッド(存在する場合)が新しい所有者として選択されます。スレッド識別子はキューから外され、セマフォは再びロックされます。ただし、threading.Semaphoreでは、キューではなくセットが使用されるため、スレッドがブロックされた順序は格納されません - any threadを次の所有者として選択することができます。

Java( Java.util.concurrent

  • A lockJava.util.concurrent.ReentrantLock)は、リエントラントロックも実装しているという点で、C/C++のpthread_mutex_tおよびPythonのthreading.RLockとほとんど同じです。プロセス間でロックを共有することは、JVMが仲介者として機能するため、Javaでは困難です。スレッドが所有していないロックをロック解除しようとすると、IllegalMonitorStateExceptionがスローされます。
  • A mutexはロックと同じです(この用語はJavaではあまり使われません)。
  • A semaphoreJava.util.concurrent.Semaphore)はsem_tthreading.Semaphoreとほとんど同じです。 Javaセマフォのコンストラクタは、待機スレッドを格納するためにset(false)またはqueue(true)のどちらを使用するかを制御する fairness booleanパラメータを受け入れます。

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

18
James Lawson

例を挙げて説明します。

Lock:lockを使用する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を使うでしょう(最後に、ノードを削除している間に別のスレッドに何かをさせたいです)。別のスレッドが同じMutexを取得しようとした場合にMutexを取得すると、Mutexを取得したSAMEスレッドがそれを解放するまでスリープします。

グローバルミューテックスを作成する良い例は@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)
}

これが時間を節約することを願っています。

13

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

ミューテックスはバイナリセマフォと本質的に同じものであり、時には同じ基本的な実装を使用します。違いは次のとおりです。

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

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

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

7
andy boot

私の理解するところでは、ミューテックスは単一のプロセス内でのみ使用されますが、その多数のスレッド間で使用されます。

また、ミューテックスはバイナリ(ロックまたはロック解除)ですが、セマフォはカウントの概念、または複数のロックおよびロック解除要求のキューを持ちます。

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

4
Bruce Penswick

例として、ベースケースとしてLinux版でCプログラミングを使用する。

ロック:

•通常、ロックされているかロックされていない操作中の非常に単純な構成バイナリ

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

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

•通常はアトミック操作に依存します。テストと設定、比較とスワップ、取り出しと追加など.

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

ファイルロック:

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

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

•例:flock、fcntlなど。

ミューテックス:

•ミューテックス関数呼び出しは通常カーネル空間で動作し、システムコールを引き起こします。

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

•ミューテックスは再帰的ではありません(例外:PTHREAD_MUTEX_RECURSIVE)。

•通常、条件変数と関連して使用され、引数としてたとえば次のものに渡されます。 pthread_cond_signal、pthread_cond_waitなど.

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

セマフォ:

•これはカーネルで管理されている整数で、その値はゼロ以下にはなりません。

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

•セマフォの値は、1より大きい値に設定されることがあります。その場合、値は通常、利用可能なリソースの数を示します。

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

2
Judayle Dsouza