私のプロジェクトでは、非常によく書かれている可能性のあるリソース上のスレッド間の通信を必要とするポイントに達しているため、同期は必須です。ただし、基本レベル以外の同期については本当に理解していません。
このリンクの最後の例を検討してください。 http://www.bogotobogo.com/cplusplus/C11/7_C11_Thread_Sharing_Memory.php
#include <iostream>
#include <thread>
#include <list>
#include <algorithm>
#include <mutex>
using namespace std;
// a global variable
std::list<int>myList;
// a global instance of std::mutex to protect global variable
std::mutex myMutex;
void addToList(int max, int interval)
{
// the access to this function is mutually exclusive
std::lock_guard<std::mutex> guard(myMutex);
for (int i = 0; i < max; i++) {
if( (i % interval) == 0) myList.Push_back(i);
}
}
void printList()
{
// the access to this function is mutually exclusive
std::lock_guard<std::mutex> guard(myMutex);
for (auto itr = myList.begin(), end_itr = myList.end(); itr != end_itr; ++itr ) {
cout << *itr << ",";
}
}
int main()
{
int max = 100;
std::thread t1(addToList, max, 1);
std::thread t2(addToList, max, 10);
std::thread t3(printList);
t1.join();
t2.join();
t3.join();
return 0;
}
この例は、3つのスレッド(2つのライターと1つのリーダー)が共通のリソース(リスト)にアクセスする方法を示しています。
2つのグローバル関数が使用されます。1つは2つのライタースレッドで使用され、もう1つはリーダースレッドで使用されます。両方の関数は、lock_guardを使用して、同じリソースであるリストをロックダウンします。
今、私が頭をかき回すことができないものです:リーダーは、2つのライタースレッドとは異なるスコープでロックを使用しますが、それでも同じリソースをロックダウンします。これはどのように機能しますか?ミューテックスに関する私の限られた理解は、ライター関数に役立ちます。まったく同じ関数を使用する2つのスレッドがあります。私が理解できるのは、あなたが保護区域に入ろうとしているときにチェックが行われ、他の誰かがすでに中にいるなら、あなたは待つことです。
しかし、スコープが異なる場合はどうでしょうか?これは、プロセス自体よりも強力なメカニズムがあることを示し、「ランタイム」スレッドの実行をブロックするランタイム環境があります。しかし、c ++にはそのようなことはないと思っていました。だから私は途方に暮れています。
ここで何が起こっているのでしょうか?
myMutex
はグローバルであり、myList
を保護するために使用されます。 guard(myMutex)
は単にロックを有効にし、ブロックからの出口が破壊を引き起こし、ロックを無効にします。 guard
は、ロックを有効または無効にする便利な方法です。
これにより、mutex
はデータを保護しません。 a wayを提供するだけでデータを保護します。データを保護するのは設計パターンです。したがって、リストを次のように変更する独自の関数を作成すると、mutex
はそれを保護できません。
void addToListUnsafe(int max, int interval)
{
for (int i = 0; i < max; i++) {
if( (i % interval) == 0) myList.Push_back(i);
}
}
ロックは、データにアクセスする必要のあるすべてのコードがアクセス前にロックに関与し、完了後にロックが解除された場合にのみ機能します。すべてのアクセスの前後にロックを有効または無効にするこのデザインパターンがデータを保護します(myList
)
なぜmutex
を使用するのか、なぜbool
を使用しないのか疑問に思うでしょう。はい、できますが、bool
変数が以下のリストを含むがこれらに限定されない特定の特性を示すことを確認する必要があります。
「パフォーマンスの低下」を犠牲にして、「より良いロック」(プロセス間、スレッド間、マルチプロセッサ、シングルプロセッサなど)を提供するさまざまなsynchronization
メカニズムがあるため、常にロックメカニズムを選択する必要があります。あなたの状況にちょうど十分です。
関連する行を見てみましょう。
std::lock_guard<std::mutex> guard(myMutex);
lock_guard
はglobal mutex myMutex
を参照していることに注意してください。つまり、3つのスレッドすべてに対して同じミューテックスです。 lock_guard
が行うことは、基本的に次のとおりです。
myMutex
をロックし、それへの参照を保持します。myMutex
のロックが解除されます。ミューテックスは常に同じものであり、スコープとは関係ありません。 lock_guard
のポイントは、ミューテックスのロックとロック解除を簡単にすることです。たとえば、手動でlock
/unlock
を実行したが、関数が途中で例外をスローした場合、unlock
ステートメントに到達することはありません。そのため、手動でyoを行うと、ミューテックスがalwaysロック解除されていることを確認する必要があります。一方、lock_guard
オブジェクトは、関数の終了方法に関係なく、関数が終了するたびに自動的に破棄されます。
これがまさにロックの機能です。スレッドがロックを取得する場合、コード内のどこにあるかに関係なく、別のスレッドがロックを保持している場合、スレッドは順番を待つ必要があります。スレッドがロックを解放すると、コード内のどこにあるかに関係なく、別のスレッドがそのロックを取得する場合があります。
ロックはコードではなくデータを保護します。保護されたデータにアクセスするすべてのコードがロックを保持している間に、同じデータにアクセスする可能性があるanyコードから他のスレッドを除外することにより、保護されたデータにアクセスします。