c++多線程之互斥量(mutex)、鎖(lock,unlock,lock_guard)的應用

#include "stdafx.h"
#include<iostream>
#include<map>
#include<vector>
#include<string>
#include<thread>
using namespace std;

void myprint(int num){
	cout << "子線程開始執行,子線程編號=" << num << endl;

	cout << "子線程結束執行,子線程編號=" << num << endl;

}

vector<int>global_v = { 1,2,3 };
int main()
{
	vector<thread>mythreads;
	for (int i = 0; i < 10; i++)
	{
		mythreads.push_back(thread(&myprint,i));//創建10個子線程,每個子線程的入口函數統一爲myprint

	}
	for (auto it = mythreads.begin(); it!=mythreads.end(); it++)
	{
		it->join();//等待10個子線程都返回
	}


	cout << "主線程結束執行" << endl;
	system("pause");
	return 0;
}

多線程中,多個線程對共享的數據進行訪問,應該是最常見的應用。

如果多個線程都只是對共享數據進行讀操作,還不會有問題,但是如果有的線程讀數據,有的線程寫數據,這時候就會出現問題。比如A線程寫數據,但是寫的這個過程進行到一半,B線程就去讀,這個時候程序就會崩潰。這個時候就需要互斥量的出場啦!

mutex

解決方案如下:

#include "stdafx.h"
#include<iostream>
#include<map>
#include<vector>
#include<string>
#include<thread>
#include<list>//雙向鏈表
#include<mutex>
#include <deque>
using namespace std;

class A
{
public:
	//把收到的消息(假設是一個int型整數)放到一個隊列的子線程函數
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			cout << "inMsgRecvQueue()執行,插入一個元素" << i << endl;
			//my_mutex.lock();
			lock_guard<mutex>my_guard(my_mutex);
			msgRecvQueue.push_back(i);
			//my_mutex.unlock();
		}
	}
	void outMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			
			my_mutex.lock();
			if (!msgRecvQueue.empty())
			{

				int command = msgRecvQueue.front();
				msgRecvQueue.pop_front();
				my_mutex.unlock();
				cout << "outMsgRecvQueue()執行,取出一個元素" << command << endl;

			}
			else
			{
				my_mutex.unlock();
				cout << "outMsgRecvQueue()執行,但隊列爲空" << endl;


			}



		}

	}
private:
	list<int>msgRecvQueue;//消息隊列,list、deque均可
	mutex my_mutex;//創建一個互斥量成員變量
};

int main()
{
	A myobja;

	thread myOutMsgObj(&A::outMsgRecvQueue, &myobja);
	thread myInMsgObj(&A::inMsgRecvQueue, &myobja);


	myInMsgObj.join();
	myOutMsgObj.join();
	cout << "主線程結束執行" << endl;
	system("pause");
	return 0;
}

注意事項:
1.mutex的成員函數lock()和unlock()必須同時成對出現,否則必完蛋。
2.如果使用lock_guard,就類似智能指針的用法,當lock_guard的對象生命週期結束時,會調用析構函數,而在析構函數中實際是調用了unlock()。
3.一旦使用了lock_guard,同一個地方就不能再使用lock()和unlock()。

std::lock()函數模板

std::lock()用來處理多個互斥量的時候纔出場;
能力:一次鎖住兩個或兩個以上的互斥量;
它可以避免在多個線程中,因爲鎖的順序問題導致出現死鎖的風險。
std::lock():如果互斥量中一個沒鎖住,子線程就阻塞,等到所有的互斥量都鎖住,子線程才能往下走。即要麼兩個互斥量都鎖住,要麼兩個互斥量都沒鎖住,如果只鎖了一個,另外一個沒鎖成功,則它立即把已經鎖住的互斥量解鎖。

先放一個因爲鎖的順序問題導致出現死鎖的例子:

class A
{
public:
	//把收到的消息(假設是一個int型整數)放到一個隊列的子線程函數
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			cout << "inMsgRecvQueue()執行,插入一個元素" << i << endl;
			my_mutex1.lock();
			my_mutex2.lock();
			msgRecvQueue.push_back(i);
			my_mutex1.unlock();
			my_mutex2.unlock();
		}
	}
	void outMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			my_mutex2.lock();
			my_mutex1.lock();
			if (!msgRecvQueue.empty())
			{

				int command = msgRecvQueue.front();
				msgRecvQueue.pop_front();
				my_mutex1.unlock();
				my_mutex2.unlock();
				cout << "outMsgRecvQueue()執行,取出一個元素" << command << endl;

			}
			else
			{
				my_mutex1.unlock();
				my_mutex2.unlock();
				cout << "outMsgRecvQueue()執行,但隊列爲空" << endl;


			}



		}

	}
private:
	list<int>msgRecvQueue;//消息隊列,list、deque均可
	mutex my_mutex1;//創建一個互斥量成員變量
	mutex my_mutex2;
};

int main()
{
	A myobja;

	thread myOutMsgObj(&A::outMsgRecvQueue, &myobja);
	thread myInMsgObj(&A::inMsgRecvQueue, &myobja);


	myInMsgObj.join();
	myOutMsgObj.join();
	cout << "主線程結束執行" << endl;
	system("pause");
	return 0;
}

使用std::lock()解決方案如下:

class A
{
public:
	//把收到的消息(假設是一個int型整數)放到一個隊列的子線程函數
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			cout << "inMsgRecvQueue()執行,插入一個元素" << i << endl;
			lock(my_mutex1, my_mutex2);
			msgRecvQueue.push_back(i);
			my_mutex1.unlock();
			my_mutex2.unlock();
		}
	}
	void outMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			lock(my_mutex1,my_mutex2);
			if (!msgRecvQueue.empty())
			{

				int command = msgRecvQueue.front();
				msgRecvQueue.pop_front();
				my_mutex1.unlock();
				my_mutex2.unlock();
				cout << "outMsgRecvQueue()執行,取出一個元素" << command << endl;

			}
			else
			{
				my_mutex1.unlock();
				my_mutex2.unlock();
				cout << "outMsgRecvQueue()執行,但隊列爲空" << endl;


			}



		}

	}
private:
	list<int>msgRecvQueue;//消息隊列,list、deque均可
	mutex my_mutex1;//創建一個互斥量成員變量
	mutex my_mutex2;
};

int main()
{
	A myobja;

	thread myOutMsgObj(&A::outMsgRecvQueue, &myobja);
	thread myInMsgObj(&A::inMsgRecvQueue, &myobja);


	myInMsgObj.join();
	myOutMsgObj.join();
	cout << "主線程結束執行" << endl;
	system("pause");
	return 0;
}

std::lock_guard的std::adopt_lock參數

std::adopt_lock是個結構體對象,起一個標記作用:作用是表示這個互斥量已經執行過lock()了,不需要在lock_guard<mutex>構造函數裏面再對mutex的對象進行lock()了。

class A
{
public:
	//把收到的消息(假設是一個int型整數)放到一個隊列的子線程函數
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			cout << "inMsgRecvQueue()執行,插入一個元素" << i << endl;
			lock(my_mutex1, my_mutex2);
			lock_guard<mutex>my_guard1(my_mutex1,adopt_lock);
			lock_guard<mutex>my_guard2(my_mutex2, adopt_lock);
			msgRecvQueue.push_back(i);
			
		}
	}
	void outMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			lock(my_mutex1,my_mutex2);
			lock_guard<mutex>my_guard1(my_mutex1, adopt_lock);
			lock_guard<mutex>my_guard2(my_mutex2, adopt_lock);
			if (!msgRecvQueue.empty())
			{

				int command = msgRecvQueue.front();
				msgRecvQueue.pop_front();
				cout << "outMsgRecvQueue()執行,取出一個元素" << command << endl;

			}
			else
			{
				
				cout << "outMsgRecvQueue()執行,但隊列爲空" << endl;


			}



		}

	}
private:
	list<int>msgRecvQueue;//消息隊列,list、deque均可
	mutex my_mutex1;//創建一個互斥量成員變量
	mutex my_mutex2;
};

int main()
{
	A myobja;

	thread myOutMsgObj(&A::outMsgRecvQueue, &myobja);
	thread myInMsgObj(&A::inMsgRecvQueue, &myobja);


	myInMsgObj.join();
	myOutMsgObj.join();
	cout << "主線程結束執行" << endl;
	system("pause");
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章