当前位置: 首页 > 工具软件 > UNIQUE > 使用案例 >

unique_lock 详解

彭骏
2023-12-01

(1) unique_lock 取代lock_guard

(2)unique_lock的第二个参数

2.1.std::adopt_lock

2.2 std::try_to_lock

2.3 std::defer_lock

(3) unique_lock的成员函数

3.1 lock

3.2 unlock()

3.3 try_lock()

3.4 release()

(4) unique_lock 所有权的传递

unique_lock 取代lock_guard

unique_lock 是一个类模板,工作中,一般使用lock_guard(推荐使用);lock_guard取代了mutex的lock() 和 unlock() 的函数。

unique_lock比lock_guard灵活了很多;效率上差一点,内存上多一点。

// test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <list>
#include <thread>
#include <chrono>
#include <mutex>

using namespace std;

class A
{
public:
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			cout << "inMsgRecvQueue 线程执行,插入一个元素:" << i << endl;

			//lock_guard<mutex> sbguard1(my_mutex1);
			//lock_guard<mutex> sbguard2(my_mutex2);

			unique_lock<mutex> sbguard1(my_mutex1);
			unique_lock<mutex> sbguard2(my_mutex2);

			msgRecvQueue.push_back(i);

		}
	}

	bool outMsgLULProc(int& command)
	{
		//lock_guard<mutex> sbguard1(my_mutex1);
		//lock_guard<mutex> sbguard2(my_mutex2);

		unique_lock<mutex> sbguard1(my_mutex1);
		unique_lock<mutex> sbguard2(my_mutex2);

		if (!msgRecvQueue.empty())
		{
			command = msgRecvQueue.front();
			msgRecvQueue.pop_front();

			return true;
		}

		return false;
	}

	void outMsgRecvQueue()
	{
		int command = 0;
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgLULProc(command);
			if (result == true)
			{
				cout << "outMsgRecvQueue(),执行了,从容器中取出一个元素:" << command << endl;
			}
			else
			{
				cout << "outMsgRecvQueue 线程执行了,但是目前消息队列中的元素是为空:" << i << endl;
			}
		}
	}
private:
	list<int> msgRecvQueue;
	mutex my_mutex1;
	mutex my_mutex2;
};

int main()
{
	A myobja;
	thread myInMsgObj(&A::inMsgRecvQueue,&myobja);

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

	myInMsgObj.join();
	myOutMsgObj.join();

	cout << "main 主线程执行结束" << endl;

	return 0;
}

二、lock_guard的第二个参数

adopt_lock标记作用;表示这个互斥量已经被lock了,(你必须要把互斥量提前lock,否者会报异常)

adopt_lock标记的效果就是:假设调用方线程已经拥有了互斥的所有权(已经lock()成功了),通知lock_guard不需要在构造函数中lock这个互斥量了;

unique_lock也可以带std::adopt_lock

my_mutex1.lock()

std::unique_lock<std::mutex> sbguard1(my_mutex1,std::adopt_lock)

// test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <list>
#include <thread>
#include <chrono>
#include <mutex>

using namespace std;

class A
{
public:
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			cout << "inMsgRecvQueue 线程执行,插入一个元素:" << i << endl;

			//lock_guard<mutex> sbguard1(my_mutex1);
			//lock_guard<mutex> sbguard2(my_mutex2);

			my_mutex1.lock();
			unique_lock<mutex> sbguard1(my_mutex1,std::adopt_lock);
			unique_lock<mutex> sbguard2(my_mutex2);

			msgRecvQueue.push_back(i);

		}
	}

	bool outMsgLULProc(int& command)
	{
		//lock_guard<mutex> sbguard1(my_mutex1);
		//lock_guard<mutex> sbguard2(my_mutex2);

		unique_lock<mutex> sbguard1(my_mutex1);
		unique_lock<mutex> sbguard2(my_mutex2);

		if (!msgRecvQueue.empty())
		{
			command = msgRecvQueue.front();
			msgRecvQueue.pop_front();

			return true;
		}

		return false;
	}

	void outMsgRecvQueue()
	{
		int command = 0;
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgLULProc(command);
			if (result == true)
			{
				cout << "outMsgRecvQueue(),执行了,从容器中取出一个元素:" << command << endl;
			}
			else
			{
				cout << "outMsgRecvQueue 线程执行了,但是目前消息队列中的元素是为空:" << i << endl;
			}
		}
	}
private:
	list<int> msgRecvQueue;
	mutex my_mutex1;
	mutex my_mutex2;
};

int main()
{
	A myobja;
	thread myInMsgObj(&A::inMsgRecvQueue,&myobja);

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

	myInMsgObj.join();
	myOutMsgObj.join();

	cout << "main 主线程执行结束" << endl;

	return 0;
}

(2.2) std::try_to_lock

// test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <list>
#include <thread>
#include <chrono>
#include <mutex>

using namespace std;

class A
{
public:
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			cout << "inMsgRecvQueue 线程执行,插入一个元素:" << i << endl;

			//lock_guard<mutex> sbguard1(my_mutex1);
			//lock_guard<mutex> sbguard2(my_mutex2);

			unique_lock<mutex> sbguard1(my_mutex1,std::try_to_lock);
			if (sbguard1.owns_lock())
			{
				
			}
			else
			{
				cout << "没有拿到锁,只能做点别的事情:" << i << endl;
			}
			unique_lock<mutex> sbguard2(my_mutex2,std::try_to_lock);
			if (sbguard2.owns_lock())
			{
				msgRecvQueue.push_back(i);
			}
			else
			{
				cout << "没有拿到锁,只能做点别的事情:" << i << endl;
			}
		}
	}

	bool outMsgLULProc(int& command)
	{
		//lock_guard<mutex> sbguard1(my_mutex1);
		//lock_guard<mutex> sbguard2(my_mutex2);

		unique_lock<mutex> sbguard1(my_mutex1);
		unique_lock<mutex> sbguard2(my_mutex2);


		std::chrono::milliseconds dura(20000); //1秒 = 1000毫秒,所以20000毫秒 = 20秒
		std::this_thread::sleep_for(dura);     //休息一定的时长


		if (!msgRecvQueue.empty())
		{
			command = msgRecvQueue.front();
			msgRecvQueue.pop_front();

			return true;
		}

		return false;
	}

	void outMsgRecvQueue()
	{
		int command = 0;
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgLULProc(command);
			if (result == true)
			{
				cout << "outMsgRecvQueue(),执行了,从容器中取出一个元素:" << command << endl;
			}
			else
			{
				cout << "outMsgRecvQueue 线程执行了,但是目前消息队列中的元素是为空:" << i << endl;
			}
		}
	}
private:
	list<int> msgRecvQueue;
	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 << "main 主线程执行结束" << endl;

	return 0;
}

(2.3)std::defer_lock

unique_lock 所支持的另一个参数是:std::defer_lock(用这个defer_lock 的前提是程序员不能自己先去lock 这个mutex,否者会报异常,这点和try_to_lock一样的)

defer_lock的意思就是 并没有给mutex加锁,来介绍一下unique_lock的重要成员函数。

1.unique_lock 的成员函数 lock

// test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <list>
#include <thread>
#include <chrono>
#include <mutex>

using namespace std;

class A
{
public:
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			cout << "inMsgRecvQueue 线程执行,插入一个元素:" << i << endl;

			std::unique_lock<std::mutex> sbguard1(my_mutex1,std::defer_lock);  //没有加锁的my_mutex1
			sbguard1.lock(); //不能自己unlock,unique_lock 会自己unlock
			std::unique_lock<std::mutex> sbguard2(my_mutex2, std::defer_lock); //没有加锁的my_mutex2
			sbguard2.lock();//不能自己unlock,unique_lock 会自己unlock
			
			msgRecvQueue.push_back(i);
			
		}
	}

	bool outMsgLULProc(int& command)
	{
		//lock_guard<mutex> sbguard1(my_mutex1);
		//lock_guard<mutex> sbguard2(my_mutex2);

		unique_lock<mutex> sbguard1(my_mutex1);
		unique_lock<mutex> sbguard2(my_mutex2);


		std::chrono::milliseconds dura(20000); //1秒 = 1000毫秒,所以20000毫秒 = 20秒
		std::this_thread::sleep_for(dura);     //休息一定的时长


		if (!msgRecvQueue.empty())
		{
			command = msgRecvQueue.front();
			msgRecvQueue.pop_front();

			return true;
		}

		return false;
	}

	void outMsgRecvQueue()
	{
		int command = 0;
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgLULProc(command);
			if (result == true)
			{
				cout << "outMsgRecvQueue(),执行了,从容器中取出一个元素:" << command << endl;
			}
			else
			{
				cout << "outMsgRecvQueue 线程执行了,但是目前消息队列中的元素是为空:" << i << endl;
			}
		}
	}
private:
	list<int> msgRecvQueue;
	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 << "main 主线程执行结束" << endl;

	return 0;
}

2.unique_lock 的成员函数 unlock

引入unique_lock 的成员函数 unlock,目的就是:因为有一些非共享代码需要处理,咋们不希望一直被lock住,当共享代码处理完了之后,就立马unlock。

// test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <list>
#include <thread>
#include <chrono>
#include <mutex>

using namespace std;

class A
{
public:
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			cout << "inMsgRecvQueue 线程执行,插入一个元素:" << i << endl;

			std::unique_lock<std::mutex> sbguard1(my_mutex1,std::defer_lock);  //没有加锁的my_mutex1
			sbguard1.lock(); //不能自己unlock,unique_lock 会自己unlock
			std::unique_lock<std::mutex> sbguard2(my_mutex2, std::defer_lock); //没有加锁的my_mutex2
			
			sbguard2.lock();//不能自己unlock,unique_lock 会自己unlock

			//处理一些非共享代码
            //..........
			//.........
			sbguard2.unlock();


			sbguard2.lock();
			
			msgRecvQueue.push_back(i);

            sbguard2.unlock(); //画蛇添足,也可以
			
		}
	}

	bool outMsgLULProc(int& command)
	{
		//lock_guard<mutex> sbguard1(my_mutex1);
		//lock_guard<mutex> sbguard2(my_mutex2);

		unique_lock<mutex> sbguard1(my_mutex1);
		unique_lock<mutex> sbguard2(my_mutex2);


		//std::chrono::milliseconds dura(20000); //1秒 = 1000毫秒,所以20000毫秒 = 20秒
		//std::this_thread::sleep_for(dura);     //休息一定的时长


		if (!msgRecvQueue.empty())
		{
			command = msgRecvQueue.front();
			msgRecvQueue.pop_front();

			return true;
		}

		return false;
	}

	void outMsgRecvQueue()
	{
		int command = 0;
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgLULProc(command);
			if (result == true)
			{
				cout << "outMsgRecvQueue(),执行了,从容器中取出一个元素:" << command << endl;
			}
			else
			{
				cout << "outMsgRecvQueue 线程执行了,但是目前消息队列中的元素是为空:" << i << endl;
			}
		}
	}
private:
	list<int> msgRecvQueue;
	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 << "main 主线程执行结束" << endl;

	return 0;
}

3.3 try_lock(),尝试给互斥量加锁,如果拿不到锁,则返回false,如果拿到了锁,返回true,这个函数是不阻塞的。

// test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <list>
#include <thread>
#include <chrono>
#include <mutex>

using namespace std;

class A
{
public:
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			cout << "inMsgRecvQueue 线程执行,插入一个元素:" << i << endl;

			std::unique_lock<std::mutex> sbguard1(my_mutex1,std::defer_lock);  //没有加锁的my_mutex1
			sbguard1.lock(); //不能自己unlock,unique_lock 会自己unlock

			std::unique_lock<std::mutex> sbguard2(my_mutex2, std::defer_lock); //没有加锁的my_mutex2
			
			if (sbguard2.try_lock() == true)
			{
				msgRecvQueue.push_back(i);
			}
			else
			{
				cout << "inMsgRecvQueue() 线程没有拿到锁,只能干点别的事:" << i << endl;
			}
			
		}
	}

	bool outMsgLULProc(int& command)
	{
		//lock_guard<mutex> sbguard1(my_mutex1);
		//lock_guard<mutex> sbguard2(my_mutex2);

		unique_lock<mutex> sbguard1(my_mutex1);
		unique_lock<mutex> sbguard2(my_mutex2);


		//std::chrono::milliseconds dura(20000); //1秒 = 1000毫秒,所以20000毫秒 = 20秒
		//std::this_thread::sleep_for(dura);     //休息一定的时长


		if (!msgRecvQueue.empty())
		{
			command = msgRecvQueue.front();
			msgRecvQueue.pop_front();

			return true;
		}

		return false;
	}

	void outMsgRecvQueue()
	{
		int command = 0;
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgLULProc(command);
			if (result == true)
			{
				cout << "outMsgRecvQueue(),执行了,从容器中取出一个元素:" << command << endl;
			}
			else
			{
				cout << "outMsgRecvQueue 线程执行了,但是目前消息队列中的元素是为空:" << i << endl;
			}
		}
	}
private:
	list<int> msgRecvQueue;
	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 << "main 主线程执行结束" << endl;

	return 0;
}

4.release()  返回它所管理的mutex对象指针,并释放所有权;也就是说,这个unique_lock 和 mutex不再有关系。

//严格区分unlock()和release()的区别,不要混淆。

//如果原来mutex对象处于加锁状态,你有责任接管过来并负责解锁。(release返回的是原始mutex的指针)

// test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <list>
#include <thread>
#include <chrono>
#include <mutex>

using namespace std;

class A
{
public:
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			cout << "inMsgRecvQueue 线程执行,插入一个元素:" << i << endl;

			std::unique_lock<std::mutex> sbguard1(my_mutex1); 
			std::mutex* ptx = sbguard1.release();

			ptx->unlock();


			std::unique_lock<std::mutex> sbguard2(my_mutex2, std::defer_lock); //没有加锁的my_mutex2
			
			if (sbguard2.try_lock() == true)
			{
				msgRecvQueue.push_back(i);
			}
			else
			{
				cout << "inMsgRecvQueue() 线程没有拿到锁,只能干点别的事:" << i << endl;
			}
			
		}
	}

	bool outMsgLULProc(int& command)
	{
		//lock_guard<mutex> sbguard1(my_mutex1);
		//lock_guard<mutex> sbguard2(my_mutex2);

		unique_lock<mutex> sbguard1(my_mutex1);
		unique_lock<mutex> sbguard2(my_mutex2);


		//std::chrono::milliseconds dura(20000); //1秒 = 1000毫秒,所以20000毫秒 = 20秒
		//std::this_thread::sleep_for(dura);     //休息一定的时长


		if (!msgRecvQueue.empty())
		{
			command = msgRecvQueue.front();
			msgRecvQueue.pop_front();

			return true;
		}

		return false;
	}

	void outMsgRecvQueue()
	{
		int command = 0;
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgLULProc(command);
			if (result == true)
			{
				cout << "outMsgRecvQueue(),执行了,从容器中取出一个元素:" << command << endl;
			}
			else
			{
				cout << "outMsgRecvQueue 线程执行了,但是目前消息队列中的元素是为空:" << i << endl;
			}
		}
	}
private:
	list<int> msgRecvQueue;
	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 << "main 主线程执行结束" << endl;

	return 0;
}

4.unique_lock 所有权的传递(转移)

一般有2种方法

1.std::move(...)

2.return unique_lock对象

 类似资料: