C++11并发与多线程(6)unique_lock详解

unique_lock详解

    • (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所有权的传递

(1)unique_lock取代lock_guard

unique_lock是个类模板,工作中,一般用lock_guard(推荐使用);lock_guard取代了mutex的lock()和unlock();
unique_lock比lock_guard灵活度高,但效率上差一点,内存上多一点。
用法:直接替换lock_guard

std::unique_lock<std::mutex>sbguard1(my_mutex1);

(2)unique_lock的第二个参数

lock_guard可以带第二个参数
std::lock_guardstd::mutexsbguard1(my_mutex1,std::adopt_lock);
起标记作用

(2.1)std::adopt_lock

std::adopt_lock表示这个互斥量已经被lock了(必须要把互斥量提前lock,否则使用该参数会报异常)
通知unique_lock(lock_guard)不需要再构造函数中lock这个互斥量了。

	void inMsgRecvQueue()
	{
		for (int i = 0; i < 10000; ++i)
		{
			cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl;
			//my_mutex1.lock();//实际工程中这两个锁头代码不一定挨着,可能她们需要保护不同的数据共享块
			//my_mutex2.lock();

			std::lock(my_mutex1, my_mutex2);//相当于每个互斥量都调用了lock()
			std::unique_lock<std::mutex>sbguard1(my_mutex1,std::adopt_lock);//sbguard1时随便起的对象名
			std::unique_lock<std::mutex>sbguard2(my_mutex2,std::adopt_lock);//sbguard2时随便起的对象名

			msgRecvQueue.push_back(i);//假设这个数字i就是我收到的命令,我直接弄到消息队列中来;
			
		/*	my_mutex2.unlock();
			my_mutex1.unlock();*/

		}
		return;
	}

(2.2) std::try_to_lock

尝试用mutex的lock()去锁定这个mutex,但如果没有锁定成功,也会立即返回,并不会阻塞在那里。
用这个try_to_lock的前提是不能先去lock

void inMsgRecvQueue()
	{
		for (int i = 0; i < 10000; ++i)
		{
			cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl;			std::unique_lock<std::mutex>sbguard1(my_mutex1,std::try_to_lock);//sbguard1时随便起的对象名
			if (sbguard1.owns_lock())
			{
				msgRecvQueue.push_back(i);
			}
			else
			{
				cout << "inMsgRecvQueue()执行,但没拿到锁,只能干点别的事" << endl;
			
			}
		}
		return;
	}

(2.3) std::defer_lock

用defer_lock的前提是 不能自己先lock,否则会报异常。
defer_lock的意思就是并没有给mutex加锁:初始化了一个没有加锁的mutex

(3) unique_lock的成员函数

(3.1)lock()

void inMsgRecvQueue()
	{
		for (int i = 0; i < 10000; ++i)
		{
			cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl;	
			std::unique_lock<std::mutex>sbguard1(my_mutex1,std::defer_lock);//没有加锁的my_mutex1
			sbguard1.lock();//unique_lock能够自动unlock
			msgRecvQueue.push_back(i);
		}
		return;
	}

(3.2) unlock()

因为有一些非共享代码要处理
为什么有时候会需要unlock:因为lock住的代码段越少,执行越快,整个程序运行效率高,有人也把锁头锁住的代码多少称为锁的粒度,粒度一般用粗细来描述;
a)锁住的代码少,粒度细,执行效率高
b)锁住的代码多,粒度粗,执行效率低
要尽量选择适合粒度的代码进行保护,粒度太细可能漏掉共享数据的保护,粒度太粗,影响效率。

void inMsgRecvQueue()
	{
		for (int i = 0; i < 10000; ++i)
		{
			cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl;
			std::unique_lock<std::mutex>sbguard1(my_mutex1,std::defer_lock);//没有加锁的my_mutex1
			sbguard1.lock();//unique_lock能够自动unlock
				//处理共享代码
			sbguard1.unlock();
				//处理非共享代码
			sbguard1.lock();
				//处理共享代码
			msgRecvQueue.push_back(i);
		}
		return;
	}

(3.3)try_lock()

尝试给互斥量加锁,如果拿不到锁则返回False,如果拿到了锁,则返回True,这个函数是不阻塞的

void inMsgRecvQueue()
	{
		for (int i = 0; i < 10000; ++i)
		{
			cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl;
std::unique_lock<std::mutex>sbguard1(my_mutex1,std::defer_lock);//没有加锁的my_mutex1
				if(sbguard1.try_lock()==true)//返回表示拿到锁了
				{ 
					msgRecvQueue.push_back(i);
				}
				else
				{
					cout << "inMsgRecvQueue()执行,但没拿到锁,只能干点别的事" << endl;
				}
		}
		return;
	}

(3.4)release()

返回它所管理的mutex对象指针,并释放所有权;也就是说unique_lock和mutex不再有关系
严格区分unlock和release的区别
release之后如果原来mutex对象处于加锁状态,需要接管过来并负责解锁
release返回的是原始的mutex指针

	{
		for (int i = 0; i < 10000; ++i)
		{
			cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl;
			std::unique_lock<std::mutex>sbguard1(my_mutex1);//sbguard1时随便起的对象名
			std::mutex *ptx=sbguard1.release();//现在有责任自己解锁了
			msgRecvQueue.push_back(i);
			ptx->unlock();//自己负责mutex的解锁了
		}
		return;
	}

(4) unique_lock所有权的传递

所有权:

std::unique_lock<std::mutex>sbguard1(my_mutex1);
  • sbguard1拥有mutex1的所有权
  • sbguard1可以把自己对mutex(my_mutex1)的所有权转移给其他的unique_lock对象
  • unique_lock对象这个mutex的所有权是属于可以移动不能复制的
    所有权传递两种方法:
    (1)std::move
std::unique_lock<std::mutex>sbguard1(my_mutex1);
std::unique_lock<std::mutex>sbguard2(std::move(sbguard1));//移动语义,相当于sbguard2与my_mutex1绑定到一起了
//现在sbguard1只想空,sbguard2指向了my_mutex1

(2)return std::unique_lockstd::mutex

	std::unique_lock<std::mutex>rtn_unique_lock()
	{
		std::unique_lock < std::mutex > tmpguard(my_mutex1);
		return tmpguard;//从函数返回一个局部的unique_lock()对象是可以的,
		//返回这种局部对象tmpguard会导致系统生成临时unique_lock对象,并调用unique_lock的移动构造函数
	}


	//把收到的消息(玩家命令)入到一个队列的线程
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 10000; ++i)
		{
			cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl;
			std::unique_lock<std::mutex>sbguard1 = rtn_unique_lock();
					msgRecvQueue.push_back(i);

		return;
	}

你可能感兴趣的:(C++11并发与多线程)