4 —— unique_lock详解

  • Post author:
  • Post category:其他




一 unique_lock取代lock_guard

unique_lock是个类模板,工作中,一般使用lock_guard。因为unqiue_lock占用的内存更多,效率底一些。但是unique_lock更加灵活,它的构造函数的第二个参数可以有其它的形式,下面会提到。同时,uniuqe_lock与lock_guard一样自动对绑定的互斥量自动加锁,解锁。



二 unique_lock的构造函数

  1. std::adopt_lock

    表示被绑定的互斥量已经被lock(),同lock_guard中一样。假设mutex已经lock成功了,再来使用。
  2. std::try_to_lock

    尝试使用mutex.lock()去锁住保护数据,如果没有锁成功,也会立即返回。流程不会阻塞,会继续进行下去。用std::try_to_lock的前提是你不能将mutex先锁住。示例:
	// 把收到的消息传入队列
	void inMsgRecvQueue()
	{
		for (size_t i = 0; i < 1000; ++i)
		{
			cout << "收到消息,并放入队列 " << i << endl;

			unique_lock<mutex> my_uniq(my_mutex, std::try_to_lock);	//在另一线程等待时,这个线程不会阻塞,等待mutex加锁。	
			
			if (my_uniq.owns_lock())	//	判断是否获得锁
			{
				msgRecvQueue.push_back(i);
			}
			else
			{
				cout << i << "入队时没有获得锁" << endl;
			}
		}

		cout << "消息入队结束" << endl;
	}

	// 从队列中取出消息
	void outMsgRecvQueue()
	{
		for (size_t i = 0; i < 1000; ++i)
		{
			unique_lock<mutex> my_uniq(my_mutex);
			std::chrono::milliseconds dura(2 * 1000); // 2秒
			std::this_thread::sleep_for(dura); // 等待2秒

			if (!msgRecvQueue.empty())
			{
				// 队列不为空
				int num = msgRecvQueue.front();
				cout << "从消息队列中取出 " << num << endl;
				msgRecvQueue.pop_front();
			}
			else
			{
				// 消息队列为空
				cout << "消息队列为空 " << endl;
			}
		}

		cout << "消息出队结束" << endl;
	}
  1. std::defer_lock

    使用std::defer_lock的前提和std::try_to_lock一样,你不能提前对mutex lock()。它的意思是,初始化了一个没有加锁的mutex。通过这个参数,可以调用unique_lock的成员函数,来提前程序的灵活性,让锁的粒度越细。

那 … 什么是锁的粒度?

锁的粒度:有人把锁头锁住的代码的多少,叫作锁的粒度。粒度用粗细来描述。

锁住的代码量越少,粒度就越细,但是可能保护数据的安全。选择一个合适的粒度是高级程序员实力的体现。

举例:

			unique_lock<mutex> my_uniq(my_mutex, std::defer_lock);	
			
			my_uniq.lock();	// 加锁

			// 处理共享数据....

			my_uniq.unlock();

			// 处理非共享数据....

			my_uniq.lock();

			// 再处理共享数据....



三 unique_lock的成员函数

  1. lock()

    对mutex加锁
  2. unlock()

    对mutex解锁
  3. try_lock()

    与std::defer_lock一起使用,作用同std::try_to_lock一样,一直尝试会获得锁,如果没有获得,则返回false。示例:
	// 把收到的消息传入队列
	void inMsgRecvQueue()
	{
		for (size_t i = 0; i < 10000; ++i)
		{
			cout << "收到消息,并放入队列 " << i << endl;

			unique_lock<mutex> my_uniq(my_mutex, std::defer_lock);	
			
			if (my_uniq.try_lock() == true)	//如果取到锁
			{
				msgRecvQueue.push_back(i);
			}
			else
			{
				cout << i << "入队没有取得锁" << endl;
			}

		}

		cout << "消息入队结束" << endl;
	}
  1. release()

    返回它所管理的mutex对象指针,并释放所有权,也就是说,unique_lock与mutex不再有联系。如果之前mutex已经加锁,则要记得unlock()。示例:
	// 从队列中取出消息
	void outMsgRecvQueue()
	{
		for (size_t i = 0; i < 10000; ++i)
		{
			unique_lock<mutex> my_uniq(my_mutex);
			std::mutex *ptx = my_uniq.release();	//	解除了mutex,要自己负责mutex的unlock()

			if (!msgRecvQueue.empty())
			{
				// 队列不为空
				int num = msgRecvQueue.front();
				cout << "从消息队列中取出 " << num << endl;
				msgRecvQueue.pop_front();
			}
			else
			{
				// 消息队列为空
				cout << "消息队列为空 " << endl;
			}

			ptx->unlock();	//	这一步不能忘记
		}

		cout << "消息出队结束" << endl;
	}



四 unique_lock的所有权转移

unique_lock所有权:unique_lock对象可以将绑定的mutex转交给其它unique_lock对象。

有两种方式:1 ,同unique_ptr一样,通过移动构造函数(不能通过拷贝构造函数)拷贝

			unique_lock<mutex> my_uniq1(my_mutex);
			unique_lock<mutex> my_uniq2(std::move(my_uniq1));

2,将unique_lock作为函数返回值。

	std::unique_lock<mutex> rtn_unique_lock()
	{
		 std::unique_lock<std::mutex> tmp_uniq(my_mutex);
		 return tmp_uniq;
	}



版权声明:本文为I_just_smile原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。