バイナリセマフォとミューテックスに違いはありますか、それとも本質的に同じですか?
36 に答える
それらは同じものではありません。用途によって使い分けています!
両方のタイプのセマフォにはフル/空の状態があり、同じ API を使用しますが、使用方法は大きく異なります。
相互排除セマフォ
相互排除セマフォは、共有リソース (データ構造、ファイルなど) を保護するために使用されます。
Mutex セマフォは、それを受け取るタスクによって「所有」されます。タスク B が現在タスク A によって保持されているミューテックスを semGive しようとすると、タスク B の呼び出しはエラーを返し、失敗します。
ミューテックスは常に次のシーケンスを使用します。
- セムテイク - クリティカル セクション - セムギブ
簡単な例を次に示します。
スレッドA スレッドB ミューテックスを取る データへのアクセス ... Take Mutex <== ブロックします ... Mutex アクセス データを与える <== ブロック解除 ... ミューテックスを与える
バイナリ セマフォ
バイナリ セマフォは、まったく別の問題に対処します。
- タスク B は、何かが起こるのを待って保留中です (たとえば、センサーが作動するなど)。
- センサーがトリップし、割り込みサービス ルーチンが実行されます。旅行のタスクを通知する必要があります。
- タスク B を実行し、センサー トリップに対して適切なアクションを実行する必要があります。その後、待機に戻ります。
Task A Task B
... Take BinSemaphore <== wait for something
Do Something Noteworthy
Give BinSemaphore do something <== unblocks
バイナリ セマフォでは、B がセマフォを受け取り、A がセマフォを渡しても問題ないことに注意してください。
繰り返しますが、バイナリ セマフォはリソースをアクセスから保護していません。セマフォを与える行為と受け取る行為は、根本的に分離されています。
通常、同じタスクが同じバイナリ セマフォに対してギブ アンド テイクを行うことはほとんど意味がありません。
- ミューテックスを解放できるのは、ミューテックスを取得したスレッドだけです。
- バイナリ セマフォは、任意のスレッド(またはプロセス)によって通知できます。
そのため、セマフォは生産者と消費者のような同期の問題により適しています。
Windows では、バイナリ セマフォはミューテックスよりもイベント オブジェクトに似ています。
トイレの例は楽しい例えです。
ミューテックス:
トイレの鍵です。一度に 1 人が鍵を持ってトイレを占有できます。終了すると、その人はキューの次の人にキーを渡します (解放します)。
公式には、「ミューテックスは通常、複数のスレッドで同時に実行できない再入可能コードのセクションへのアクセスをシリアル化するために使用されます。ミューテックス オブジェクトは、制御されたセクションへの 1 つのスレッドのみを許可し、アクセスを試みる他のスレッドを強制します。最初のスレッドがそのセクションから出るまで待機します。」参照: Symbian 開発者ライブラリ
(mutex は、実際には値 1 を持つセマフォです。)
セマフォ:
無料の同一のトイレの鍵の数です。例として、同一の鍵と鍵を備えた 4 つのトイレがあるとします。セマフォ カウント (キーの数) は、最初は 4 に設定されています (4 つのトイレはすべて空いています)。その後、人が入ってくるとカウント値が減少します。空いているキーが残っていない場合、セマフォ カウントは 0 です。1 人がトイレを出ると、セマフォが 1 (1 つの空きキー) に増え、列の次の人に渡されます。
公式には、「セマフォは、共有リソースの同時ユーザー数を最大数に制限します。スレッドは、リソースへのアクセスを要求し (セマフォを減らします)、リソースの使用が終了したことを通知できます (セマフォを増やします)。 " 参照: Symbian 開発者ライブラリ
このトピックに関する素敵な記事:
パート 2 から:
ミューテックスは、所有権の原則という 1 つの重要な違いを除いて、バイナリ セマフォの原則に似ています。所有権とは、タスクがミューテックスをロック (取得) すると、タスクだけがロックを解除 (解放) できるという単純な概念です。タスクがロックされていない (したがって所有していない) ミューテックスをロック解除しようとすると、エラー状態が発生し、最も重要なことに、ミューテックスはロック解除されません。相互排除オブジェクトに所有権がない場合、その名前に関係なく、それはミューテックスではありません。
それらの同期セマンティクスは大きく異なります。
- ミューテックスを使用すると、特定のリソースへのアクセスをシリアル化できます。つまり、複数のスレッドが一度に 1 つずつロックを待機し、前述のように、ロックが完了するまでスレッドがロックを所有します。この特定のスレッドのみがロックを解除できます。
- バイナリ セマフォは、値が 0 と 1 のカウンタです。タスクは、いずれかのタスクが sem_post を実行するまでブロックします。セマフォは、リソースが使用可能であることをアドバタイズし、使用可能であると通知されるまで待機するメカニズムを提供します。
そのため、ミューテックスをタスクからタスクに渡されるトークンと見なし、セマフォをトラフィックの赤信号と見なすことができます (続行できることを誰かに知らせます) 。
理論的なレベルでは、意味的に違いはありません。セマフォを使用してミューテックスを実装することも、その逆も可能です (例については、こちらを参照してください)。実際には、実装は異なり、わずかに異なるサービスを提供します。
実際の違い (それらを取り巻くシステム サービスの観点から) は、ミューテックスの実装がより軽量な同期メカニズムを目指していることです。オラクルでは、ミューテックスはラッチと呼ばれ、セマフォは待機と呼ばれます。
最低レベルでは、ある種のアトミックなテストおよび設定メカニズムを使用します。これは、メモリ位置の現在の値を読み取り、ある種の条件を計算し、中断できない単一の命令でその位置の値を書き出します。これは、ミューテックスを取得して、他の誰かがあなたの前にそれを持っていないかどうかをテストできることを意味します。
典型的なミューテックスの実装には、test-and-set 命令を実行し、他の何かがミューテックスを設定したかどうかを評価するプロセスまたはスレッドがあります。ここでの重要なポイントは、スケジューラとの対話がないため、誰がロックを設定したかはわかりません (そして気にしません)。次に、タイム スライスをあきらめて、タスクが再スケジュールされたときに再試行するか、スピン ロックを実行します。スピンロックは次のようなアルゴリズムです:
Count down from 5000:
i. Execute the test-and-set instruction
ii. If the mutex is clear, we have acquired it in the previous instruction
so we can exit the loop
iii. When we get to zero, give up our time slice.
保護されたコード (クリティカル セクションと呼ばれる) の実行が終了したら、mutex 値をゼロまたは「クリア」を意味するものに設定します。複数のタスクがミューテックスを取得しようとしている場合、ミューテックスが解放された後に予定されている次のタスクがリソースにアクセスします。通常、ミューテックスを使用して、共有データ構造を更新するために、排他的アクセスが非常に短い期間だけ必要とされる同期リソースを制御します。
セマフォは、同期されたデータ構造 (通常はミューテックスを使用) であり、ミューテックス ライブラリよりも少し深くスケジューラとやり取りするカウントといくつかのシステム コール ラッパーがあります。セマフォはインクリメントおよびデクリメントされ、何か他の準備が整うまでタスクをブロックするために使用されます。この簡単な例については、生産者/消費者の問題を参照してください。セマフォは何らかの値に初期化されます。バイナリ セマフォは、セマフォが 1 に初期化される特殊なケースです。セマフォへのポストは、待機中のプロセスを起動する効果があります。
基本的なセマフォ アルゴリズムは次のようになります。
(somewhere in the program startup)
Initialise the semaphore to its start-up value.
Acquiring a semaphore
i. (synchronised) Attempt to decrement the semaphore value
ii. If the value would be less than zero, put the task on the tail of the list of tasks waiting on the semaphore and give up the time slice.
Posting a semaphore
i. (synchronised) Increment the semaphore value
ii. If the value is greater or equal to the amount requested in the post at the front of the queue, take that task off the queue and make it runnable.
iii. Repeat (ii) for all tasks until the posted value is exhausted or there are no more tasks waiting.
バイナリ セマフォの場合、2 つの主な実質的な違いは、実際のデータ構造を取り巻くシステム サービスの性質です。
編集: evan が正しく指摘したように、スピンロックはシングル プロセッサ マシンの速度を低下させます。シングル プロセッサでは、ミューテックスを保持しているプロセスが別のタスクの実行中にミューテックスをリセットすることはないため、マルチプロセッサ ボックスでのみスピンロックを使用します。スピンロックは、マルチプロセッサ アーキテクチャでのみ役立ちます。
Windows では、ミューテックスとバイナリ セマフォの間に 2 つの違いがあります。
ミューテックスは、所有権を持つスレッド、つまり以前に Wait 関数を呼び出した (または作成時に所有権を取得した) スレッドによってのみ解放できます。セマフォは、どのスレッドからでも解放できます。
スレッドは、ブロックせずにミューテックスで待機関数を繰り返し呼び出すことができます。ただし、その間にセマフォを解放せずにバイナリ セマフォで待機関数を 2 回呼び出すと、スレッドがブロックされます。
神話:
記事のカップルは、「バイナリセマフォとミューテックスは同じです」または「値1のセマフォはミューテックスです」と述べていますが、基本的な違いは、ミューテックスはそれを取得したスレッドによってのみ解放でき、他のスレッドからセマフォにシグナルを送ることができることです
キーポイント:
• スレッドは複数のロックを取得できます (Mutex)。
•ミューテックスは、再帰ミューテックスの場合にのみ複数回ロックできます。ここでは、ミューテックスのロックとロック解除は同じでなければなりません
• すでにミューテックスをロックしているスレッドが再度ミューテックスをロックしようとすると、そのミューテックスの待機リストに入り、デッドロックが発生します。
•バイナリセマフォとミューテックスは似ていますが、同じではありません。
• ミューテックスは、関連する保護プロトコルのためにコストのかかる操作です。
•ミューテックスの主な目的は、アトミックアクセスまたはリソースのロックを実現することです
Mutex は「ロック機構」に使用されます。一度に 1 つのプロセスが共有リソースを使用できる
一方
セマフォは、「I am done , now can continue」のような「シグナリング メカニズム」に使用されます。
明らかに、ミューテックスを使用して、あるスレッドのデータをロックし、別のスレッドが同時にアクセスできるようにします。呼び出したばかりlock()
で、データにアクセスしている最中であるとします。これは、他のスレッド (または同じスレッドコードの別のインスタンス) が、同じミューテックスによってロックされた同じデータにアクセスすることを期待しないことを意味します。つまり、同じスレッド コードが別のスレッド インスタンスで実行される場合、ロックにヒットすると、lock()
そこで制御フローをブロックする必要があります。これは、同じデータにアクセスし、同じミューテックスによってロックされている別のスレッド コードを使用するスレッドに適用されます。この場合、まだデータにアクセスしている途中であり、ミューテックスのロック解除に到達するまでにさらに 15 秒かかる場合があります (ミューテックス ロックでブロックされている他のスレッドのブロックが解除され、コントロールがアクセスできるようになるため)。データにアクセスします)。とにかく、さらに別のスレッドが同じミューテックスのロックを解除することを許可し、ミューテックスロックで既に待機中 (ブロック中) のスレッドがブロックを解除してデータにアクセスできるようにしますか? 私がここで言っていることが理解できますか?のとおり、普遍的な定義に合意しました!
- 「ミューテックス」では、これは起こりません。他のスレッドはスレッドのロックを解除できません
- 「バイナリセマフォ」を使用すると、これが発生する可能性があります。他のスレッドはスレッドのロックを解除できます
そのため、ミューテックスの代わりにバイナリ セマフォを使用することに非常にこだわっている場合は、ロックとロック解除の「スコープ」に細心の注意を払う必要があります。つまり、すべてのロックをヒットするすべての制御フローは、ロック解除呼び出しをヒットする必要があり、「最初のロック解除」があってはならず、常に「最初のロック」であるべきです。
Mutexは、単一の共有リソースへのアクセスを制御します。そのリソースへのアクセスをacquire()し、完了したらrelease()する操作を提供します。
セマフォは、リソースの共有プールへのアクセスを制御します。プール内のリソースの 1 つが使用可能になるまでWait()に操作を提供し、リソースがプールに戻されるとSignal()に操作を提供します。
Semaphore が保護するリソースの数が 1 より大きい場合、それはCounting Semaphoreと呼ばれます。1 つのリソースを制御する場合、ブール セマフォと呼ばれます。ブールセマフォはミューテックスに相当します。
したがって、セマフォはミューテックスよりも高いレベルの抽象化です。Mutex はセマフォを使用して実装できますが、その逆はできません。
修正された質問は - 「Linux」のミューテックスと「バイナリ」セマフォの違いは何ですか?
回答: 違いは次のとおりです。i) スコープ – ミューテックスのスコープは、それを作成したプロセス アドレス空間内にあり、スレッドの同期に使用されます。一方、セマフォはプロセス空間全体で使用できるため、プロセス間同期に使用できます。
ii) Mutex はセマフォより軽量で高速です。Futexはさらに高速です。
iii) Mutex は、同じ回数解放する必要があるという条件で、同じスレッドによって複数回正常に取得できます。取得しようとしている他のスレッドはブロックされます。セマフォの場合、同じプロセスが再度取得しようとすると、一度しか取得できないためブロックされます。
Binary Semaphore と Mutex の相違点: OWNERSHIP: セマフォは、現在の所有者以外からでも通知 (投稿) できます。これは、所有者でなくても、他のスレッドから簡単に投稿できることを意味します。
セマフォは処理中のパブリック プロパティです。所有者以外のスレッドから簡単に投稿できます。この違いを太字でマークしてください。これには多くの意味があります。
http://www.geeksforgeeks.org/archives/9102 で詳しく説明しています。
Mutex
リソースへのアクセスを同期するために使用されるロック機構です。
Semaphore
シグナリングメカニズムです。
ミューテックスの代わりにバイナリセマフォを使用するかどうかは、プログラマ次第です。
ミューテックスは重要な領域をブロックしますが、セマフォはカウントします。
ミューテックスに所有者があるという事実とは別に、2つのオブジェクトは異なる使用法に最適化される場合があります。ミューテックスは、短時間だけ保持されるように設計されています。これに違反すると、パフォーマンスが低下し、スケジュールが不公平になる可能性があります。たとえば、実行中のスレッドは、別のスレッドがすでにブロックされている場合でも、ミューテックスの取得を許可される場合があります。セマフォはより公平性を提供する場合があります。または、いくつかの条件変数を使用して公平性を強制することができます。
バイナリ セマフォはミューテックスとして使用できますが、ミューテックスは、ミューテックスをロックしたプロセスのみがロックを解除することになっているという点で、より具体的なユース ケースです。この所有権の制約により、次に対する保護を提供できます。
- 偶発的なリリース
- 再帰的デッドロック
- タスクの死のデッドロック
これらの制約は速度を低下させるため、常に存在するとは限りません。コードの開発中に、これらのチェックを一時的に有効にすることができます。
たとえば、ミューテックスでエラーチェック属性を有効にすることができます。EDEADLK
同じミューテックスを 2 回ロックしようとしたりEPERM
、自分のものではないミューテックスをロック解除したりすると、ミューテックスをチェックするエラーが返されます。
pthread_mutex_t mutex;
pthread_mutexattr_t attr;
pthread_mutexattr_init (&attr);
pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
pthread_mutex_init (&mutex, &attr);
初期化したら、これらのチェックを次のようにコードに配置できます。
if(pthread_mutex_unlock(&mutex)==EPERM)
printf("Unlock failed:Mutex not owned by this thread\n");
上記の投稿を読んだ後、コンセプトは私には明らかでした。しかし、いくつかの疑問が残りました。それで、私はこの小さなコードを書きました。
セマフォを取得せずにセマフォを渡そうとすると、それは通過します。ただし、ミューテックスを取得せずに渡そうとすると、失敗します。これを Windows プラットフォームでテストしました。USE_MUTEX を有効にして、MUTEX を使用して同じコードを実行します。
#include <stdio.h>
#include <windows.h>
#define xUSE_MUTEX 1
#define MAX_SEM_COUNT 1
DWORD WINAPI Thread_no_1( LPVOID lpParam );
DWORD WINAPI Thread_no_2( LPVOID lpParam );
HANDLE Handle_Of_Thread_1 = 0;
HANDLE Handle_Of_Thread_2 = 0;
int Data_Of_Thread_1 = 1;
int Data_Of_Thread_2 = 2;
HANDLE ghMutex = NULL;
HANDLE ghSemaphore = NULL;
int main(void)
{
#ifdef USE_MUTEX
ghMutex = CreateMutex( NULL, FALSE, NULL);
if (ghMutex == NULL)
{
printf("CreateMutex error: %d\n", GetLastError());
return 1;
}
#else
// Create a semaphore with initial and max counts of MAX_SEM_COUNT
ghSemaphore = CreateSemaphore(NULL,MAX_SEM_COUNT,MAX_SEM_COUNT,NULL);
if (ghSemaphore == NULL)
{
printf("CreateSemaphore error: %d\n", GetLastError());
return 1;
}
#endif
// Create thread 1.
Handle_Of_Thread_1 = CreateThread( NULL, 0,Thread_no_1, &Data_Of_Thread_1, 0, NULL);
if ( Handle_Of_Thread_1 == NULL)
{
printf("Create first thread problem \n");
return 1;
}
/* sleep for 5 seconds **/
Sleep(5 * 1000);
/*Create thread 2 */
Handle_Of_Thread_2 = CreateThread( NULL, 0,Thread_no_2, &Data_Of_Thread_2, 0, NULL);
if ( Handle_Of_Thread_2 == NULL)
{
printf("Create second thread problem \n");
return 1;
}
// Sleep for 20 seconds
Sleep(20 * 1000);
printf("Out of the program \n");
return 0;
}
int my_critical_section_code(HANDLE thread_handle)
{
#ifdef USE_MUTEX
if(thread_handle == Handle_Of_Thread_1)
{
/* get the lock */
WaitForSingleObject(ghMutex, INFINITE);
printf("Thread 1 holding the mutex \n");
}
#else
/* get the semaphore */
if(thread_handle == Handle_Of_Thread_1)
{
WaitForSingleObject(ghSemaphore, INFINITE);
printf("Thread 1 holding semaphore \n");
}
#endif
if(thread_handle == Handle_Of_Thread_1)
{
/* sleep for 10 seconds */
Sleep(10 * 1000);
#ifdef USE_MUTEX
printf("Thread 1 about to release mutex \n");
#else
printf("Thread 1 about to release semaphore \n");
#endif
}
else
{
/* sleep for 3 secconds */
Sleep(3 * 1000);
}
#ifdef USE_MUTEX
/* release the lock*/
if(!ReleaseMutex(ghMutex))
{
printf("Release Mutex error in thread %d: error # %d\n", (thread_handle == Handle_Of_Thread_1 ? 1:2),GetLastError());
}
#else
if (!ReleaseSemaphore(ghSemaphore,1,NULL) )
{
printf("ReleaseSemaphore error in thread %d: error # %d\n",(thread_handle == Handle_Of_Thread_1 ? 1:2), GetLastError());
}
#endif
return 0;
}
DWORD WINAPI Thread_no_1( LPVOID lpParam )
{
my_critical_section_code(Handle_Of_Thread_1);
return 0;
}
DWORD WINAPI Thread_no_2( LPVOID lpParam )
{
my_critical_section_code(Handle_Of_Thread_2);
return 0;
}
セマフォがリソースを所有していなくても、「リソースを使用して完了した」ことを知らせることができるという事実は、セマフォの場合、所有とシグナリングの間に非常に疎結合があると私に思わせます。
ミューテックス
通常、ミューテックスは、複数のスレッドで同時に実行できない再入可能コードのセクションへのアクセスをシリアル化するために使用されます。ミューテックス オブジェクトは、制御されたセクションへの 1 つのスレッドのみを許可し、そのセクションへのアクセスを試みる他のスレッドを、最初のスレッドがそのセクションから出るまで待機させます。ミューテックスの適切な使用は、共有リソースを保護することです。意図しない副作用。異なる優先度で動作し、ミューテックスを介して調整する 2 つの RTOS タスクは、優先度の逆転の機会を生み出します。Mutex はユーザー空間で機能します。
セマフォ
セマフォはシグナリングメカニズムです。セマフォは、共有リソースの同時ユーザー数を最大数まで制限します。スレッドは、リソースへのアクセスを要求し (セマフォを減らします)、リソースの使用が終了したことを通知できます (セマフォを増やします)。複数のスレッドが共有リソースにアクセスできるようにします。セマフォの正しい使用法は、あるタスクから別のタスクへのシグナル伝達です。セマフォは、割り込みサービス ルーチン(ISR) からタスクへのシグナル伝達にも使用できます。セマフォのシグナリングは非ブロッキング RTOS 動作であるため、ISR は安全です。この手法により、エラーが発生しやすいタスク レベルで割り込みを無効にする必要がなくなるためです。これはカーネル空間で機能します。
センシティブなコードやデータの保護にはミューテックス、同期にはセマフォを利用します。センシティブなコードを保護することも実用化できますが、V操作によって他のスレッドによる保護が解除されるリスクがあるかもしれません。バイセマフォとミューテックスの違いは所有権です。たとえば、トイレでは、ミューテックスはトイレに入ってドアをロックできるようなもので、男性が出るまで誰も入ることができません。バイセマフォは入ることができるようなものですトイレを閉めてドアをロックしますが、管理者にドアを開けるように頼むことで誰かが入ることができます。ばかげています。
セマフォとは異なり、ミューテックスには所有権があります。ミューテックスのスコープ内のどのスレッドも、ロック解除されたミューテックスを取得し、コードの同じクリティカルセクションへのアクセスをロックできますが、ミューテックスをロックしたスレッドのみがロックを解除する必要があります。
ここで多くの人が言及しているように、ミューテックスはコードの重要な部分 (別名クリティカル セクション) を保護するために使用されます。ミューテックスを取得 (ロック) し、クリティカル セクションに入り、ミューテックスを解放 (ロック解除)すべて同じスレッドで行います。
セマフォを使用している間、別のスレッド (スレッド B など) が何らかのタスクを完了するまでスレッドをセマフォ (スレッド A など) で待機させ、スレッド A のセマフォを設定して待機を停止し、そのタスクを続行できます。
答えは、ターゲット OS によって異なる場合があります。たとえば、私がよく知っている少なくとも 1 つの RTOS 実装では、すべてが同じスレッド コンテキスト内からのものである限り、単一の OS ミューテックスに対して複数の連続した「取得」操作を許可します。別のスレッドがミューテックスを取得できるようにするには、複数の get を同数の put に置き換える必要があります。 これは、スレッド コンテキストに関係なく、一度に 1 つの get しか許可されないバイナリ セマフォとは異なります。
このタイプのミューテックスの背後にある考え方は、一度に 1 つのコンテキストのみがデータを変更できるようにすることでオブジェクトを保護するというものです。スレッドがミューテックスを取得し、オブジェクトをさらに変更する関数を呼び出したとしても (そして、独自の操作でプロテクター ミューテックスを取得/配置した場合)、操作はすべて単一のスレッドで行われているため、安全である必要があります。
{
mutexGet(); // Other threads can no longer get the mutex.
// Make changes to the protected object.
// ...
objectModify(); // Also gets/puts the mutex. Only allowed from this thread context.
// Make more changes to the protected object.
// ...
mutexPut(); // Finally allows other threads to get the mutex.
}
もちろん、この機能を使用するときは、1 つのスレッド内のすべてのアクセスが本当に安全であることを確認する必要があります。
このアプローチがどれほど一般的か、または私がよく知っているシステム以外に適用できるかどうかはわかりません。この種のミューテックスの例については、ThreadX RTOS を参照してください。
ミューテックスとバイナリセマフォは同じ使い方ですが、実際には違います。
ミューテックスの場合、それをロックしたスレッドのみがロックを解除できます。他のスレッドがそれをロックするようになると、待機します。
セマフォンの場合はそうではありません。セマフォは特定のスレッド ID に結び付けられていません。
上記のほとんどすべてが正しいと言っていました。また、誰かがまだ疑問を持っているかどうかを明確にするために少し試してみましょう.
- Mutex -> シリアル化に使用
- セマフォ -> 同期。
両方の目的は異なりますが、注意深いプログラミングにより、両方を介して同じ機能を実現できます。
標準的な例 -> 生産者と消費者の問題。
initial value of SemaVar=0
Producer Consumer
--- SemaWait()->decrement SemaVar
produce data
---
SemaSignal SemaVar or SemaVar++ --->consumer unblocks as SemVar is 1 now.
私が明確にできることを願っています。