C++マルチスレッド3-共有データ操作保護


ディレクトリ:1.マルチスレッド操作共有データによる問題2.マルチスレッド操作の共有データを解決する方法:ロック3.反発量mutexの概念と使い方4.lock普通錠の使い方5.lock_guardクラステンプレートの使い方6.デッドロックの概念と解決7.unique_ロックの使い方
1.マルチスレッド操作共有データからの問題コードの例:
//   Test::set             ,          vector size,         。
#include 
#include 
#include 
#include 
#include 

using namespace std;

class Test
{
public:
	void set(const int index)
	{
		string str = "hello" + to_string(index);
		str_mv.push_back(str);
		cout << "  id=" << std::this_thread::get_id() << ",size=" << str_mv.size() << endl;
	}
	
private:
	vector<string> str_mv;
};

int main()
{
	Test test;
	thread th[10000];
	for (int i=0; i<10000; i++)
	{
		th[i] = thread(&Test::set, &test, i);
	}
	
	for (int i=0; i<10000; i++)
	{
		if (th[i].joinable())
			th[i].join();
	}
	return 0;
}

//    
root@epc:/home/share/test#  ./test
...
  id=139743976642304,size=4086
  id=139743968249600,size=4087
  id=139743959856896,size=4088
  id=139743951464192,size=4089
Segmentation fault
root@epc:/home/share/test# 

2.マルチスレッド操作共有データを解決する方法:マルチスレッド操作共有データをロックする場合、複数のスレッドが同時に共有データにアクセスする場合があり、それらの操作がただ読むだけであれば問題はないが、書き込みと読み取りを同時に行うと問題が発生し、メモリアクセスエラーが発生しやすい.そこで,ロックメカニズムにより,スレッドがキューに並んで共有データにアクセスできるようにした.
3.反発量mutexの概念と用法反発量の役割は、共有リソースまたはセグメントコードを管理することである.ロック解除とロック解除の2つの状態があります.複数のスレッドが共有リソースにアクセスすると、ランダムに1つのスレッドを選択して共有リソースを処理するロックが割り当てられ、残りのスレッドは反発量にブロックされ、スレッドが完了すると、反発量は他のスレッドにロックされ、すべてのスレッドが処理されるまで順次ループされます.反発量のヘッダファイルはmutexであり、すべてのスレッドに対して一意であるため、グローバル変数またはクラス静的メンバーとして宣言する必要があります.
//         
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

class Test
{
public:
	void set(const int index)
	{
		mutex_m.lock();
		string str = "hello" + to_string(index);
		str_mv.push_back(str);
		cout << "  id=" << std::this_thread::get_id() << ",size=" << str_mv.size() << endl;
		mutex_m.unlock();
	}
	
private:
	vector<string> str_mv;
	static mutex mutex_m;
};

mutex Test::mutex_m;

int main()
{
	Test test;
	thread th[10000];
	for (int i=0; i<10000; i++)
	{
		th[i] = thread(&Test::set, &test, i);
	}
	
	for (int i=0; i<10000; i++)
	{
		if (th[i].joinable())
			th[i].join();
	}
	return 0;
}
//    
root@epc:/home/share/test#  ./test
...
  id=140531608835840,size=9998
  id=140531617228544,size=9999
  id=140531625621248,size=10000
root@epc:/home/share/test# 

4.lock通常ロックの用法反発量lock()を使用してリソースをロックした後、必ずunlock()を呼び出してロックを解除し、順序を逆さまにしてはいけない.そうしないと、実行プログラムは異常を報告する.lock()およびunlock()の位置は、ロックリソースの範囲を限定する.しかし、プログラマーはunlock()の使用を忘れやすい場合が多いため、C++はlock_のようなスマートポインタメカニズムに似たテンプレートクラスを導入しています.guardは、オブジェクトの生存期間が終了すると、構造関数がunlock()関数を呼び出します.異常の結果は次のとおりです.root@epc:/home/share/test# ./testスレッドid=139657018976000、size=1 terminate called after throwing an instance of'std::system_error’ what(): Resource temporarily unavailable Aborted root@epc:/home/share/test#
5.lock_guardクラステンプレートの使い方lock_guardは比較的簡単で効率的で,関数を構築する際にロックを取得し,関数を解析する際にロックを解放する.
//lock_guard      
class Test
{
public:
	void set(const int index)
	{
		lock_guard<mutex> lock(mutex_m);
		string str = "hello" + to_string(index);
		str_mv.push_back(str);
		cout << "  id=" << std::this_thread::get_id() << ",size=" << str_mv.size() << endl;
	}
	
private:
	vector<string> str_mv;
	static mutex mutex_m;
};

mutex Test::mutex_m;

6.デッドロックの概念とデッドロックを解決する最も直感的な現象は、プログラムが反応せず、このときすべてのスレッドがブロックされた状態に陥ることである.次の例の解析によれば、デッドロックの根本的な原因は、複数のロックが存在する場合にロックの順序が一致しないことであり、解決策はロックの順序が一致していることである.解決策:
//       :        
class Test
{
public:
	void set()
	{
		//             mutex1_m  mutex2_m ;
		lock_guard<mutex> lock1(mutex1_m);	
		lock_guard<mutex> lock2(mutex2_m);
		string str = "hello";
		str_mv.push_back(str);
		cout << "  id=" << std::this_thread::get_id() << "  set" << endl;
	}
	
	void get()
	{
		//             mutex1_m  mutex2_m ;
		lock_guard<mutex> lock1(mutex1_m);
		lock_guard<mutex> lock2(mutex2_m);
		cout << "  id=" << std::this_thread::get_id() << "  get" << endl;
	}
	
private:
	vector<string> str_mv;
	static mutex mutex1_m;
	static mutex mutex2_m;
};

デッドロック・コードは、次のコードの例を示します.
//           :
//  A  set      mutex1_m     mutex2_m ,  B  get      mutex2_m     mutex1_m ;
//      ,   A   mutex1_m  ,      mutex2_m ,      B    mutex2_m ,
//      B  mutex2_m ,    B      A  mutex1_m ;          ,         。
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

class Test
{
public:
	void set()
	{
		//             mutex1_m  mutex2_m ;
		lock_guard<mutex> lock1(mutex1_m);	
		lock_guard<mutex> lock2(mutex2_m);
		string str = "hello";
		str_mv.push_back(str);
		cout << "  id=" << std::this_thread::get_id() << "  set" << endl;
	}
	
	void get()
	{
		//             mutex2_m  mutex1_m ;
		lock_guard<mutex> lock2(mutex2_m);
		lock_guard<mutex> lock1(mutex1_m);
		cout << "  id=" << std::this_thread::get_id() << "  get" << endl;
	}
	
private:
	vector<string> str_mv;
	static mutex mutex1_m;
	static mutex mutex2_m;
};

mutex Test::mutex1_m;
mutex Test::mutex2_m;

int main()
{
	Test test;
	thread th1[5000];
	thread th2[5000];
	for (int i=0; i<5000; i++)
	{
		th1[i] = thread(&Test::set, &test);
		th2[i] = thread(&Test::get, &test);
	}
	
	for (int i=0; i<5000; i++)
	{
		if (th1[i].joinable())
			th1[i].join();
		if (th2[i].joinable())
			th2[i].join();
	}
	return 0;
}

7.unique_lockの使い方は内容が多く、後で補足します.