C++并发与多线程---学习笔记(3)unique_lock(类模板)详解

c++11并发与多线程

  • 一、unique_lock(类模板)详解
    • (1)unique_lock取代lock_guard
    • (2)unique_lock的第二个参数
      • 1)std::adopt_lock
      • 2)std::try_to_lock
      • 3)std::defer_lock
    • (3)unique_lock的成员函数
      • 1)lock()
      • 2)unlock()
      • 3)try_lock()
      • 4)release()
    • (4)unique_lock所有权的传递


一、unique_lock(类模板)详解

(1)unique_lock取代lock_guard

  • unique_lock是个类模板,工作中,一般lock_guard(推荐使用),lock_guard取代了mutex的lock和unlock
  • unique_lock比lock_guard灵活很多,但效率上差一点,内存占用多一点

unique_lock具体怎么灵活请继续往下看


(2)unique_lock的第二个参数

1)std::adopt_lock

  • std::adopt_lock:表示这个互斥量已经被lock了(因此必须要把互斥量提前lock)
  • adopt_lock标记的效果是“假设调用方 线程已经拥有了互斥的所有权(已经lock成功)”
  • 通知lock_guard不需要再构造函数中lock这个互斥量,前提是你自己要先去lock,unique_lock也可以带adopt_lock标志,含义相同,就是不能在unique_lock()构造函数中的lock这个mutex,可以自动解锁,跟lock_guard一一样
#include
#include
#include
#include
#include
#include

using namespace std;

class Example {
public:
	void messageIn() {
		for (int i = 0; i < 10000; i++)
		{
			myMutex.lock();
			unique_lock<mutex>guard(myMutex, adopt_lock);
			cout << "messageIn(),服务器收集数据数量:" << i << endl;
			num.push_back(i);

		}
		return;
	}
	bool outMessage() {
		myMutex.lock();
		unique_lock<mutex>guard(myMutex, adopt_lock);
		if (!num.empty()) {
			num.pop_front();
			return true;
		}
		return false;
	}
	void messageOut() {
		for (int i = 0; i < 10000; i++)
		{
			bool result = outMessage();
			if (result==true) {
				cout << "messageOut()执行,服务器已发送的数据数量" << endl;
			}
			else {
				cout << "messageOut()执行,但目前消息队列中为空" << i << endl;
			}
		}
		cout << "end" << endl;
	}

private:
	list<int>num;
	mutex myMutex;
};

int main() {

	Example ex;
	thread out(&Example::messageOut, &ex);//第二个参数是 引用,保证线程里用的是同一个对象ex,(防止拷贝新的对象)
	thread in(&Example::messageIn, &ex);
	out.join();
	in.join();

	cout << "主线程运行" << endl;
	system("pause");
	return 0;
}



2)std::try_to_lock

  • 尝试用mutex的lock()去锁定这个mutex,但如果没有锁定成功会立即返回,不会阻塞在那里,前提是不能提前lock()
  • 使用try_to_lock的原因是防止其他线程锁定mutex太长时间,导致本线程一直阻塞在lock这处,owns_lock()方法时判断是否拿到锁,拿到返回true,否则返回false
class Example {
public:
	void messageIn() {
		for (int i = 0; i < 10000; i++)
		{
			unique_lock<mutex>guard(myMutex, try_to_lock);
			if (guard.owns_lock()) {
				cout << "messageIn(),服务器收集数据数量:" << i << endl;
				num.push_back(i);
			}
			else {
				cout << "没有拿到锁!!" << endl;
			}

		}
		return;
	}
	bool outMessage() {
		myMutex.lock();
		unique_lock<mutex>guard(myMutex, adopt_lock);

		std::chrono::microseconds dura(200);
		this_thread::sleep_for(dura);

		if (!num.empty()) {
			num.pop_front();
			return true;
		}
		return false;
	}
	void messageOut() {
		for (int i = 0; i < 10000; i++)
		{
			bool result = outMessage();
			if (result==true) {
				cout << "messageOut()执行,服务器已发送的数据数量" << endl;
			}
			else {
				cout << "messageOut()执行,但目前消息队列中为空" << i << endl;
			}
		}
		cout << "end" << endl;
	}

private:
	list<int>num;
	mutex myMutex;
};

代码中加入200毫秒的休息,这样更能使演示效果好


3)std::defer_lock

  • defer_lock前提是不能自己先去lock,否则汇报异常,这个参数其实即使初始化一个没有加锁的mutex,不给mutex加锁的目的是可以调用unique_lock的一些成员函数

(3)unique_lock的成员函数

1)lock()

unique_lock<mutex> guard(myMutex, defer_lock);
guard.lock();

注意事项:不用自己unlock()


2)unlock()

  • 使用unlque_lock的成员函数中的unlock()函数实际是因为有时需要处理一些非共享代码,要临时解锁处理,处理完可以再lock()
unique_lock<mutex> guard(myMutex, defer_lock);
guard.lock();
//处理一些共享代码
guard.unlock();
//处理一些非共享代码
guard.lock();
//处理一些共享代码


3)try_lock()

  • 尝试给互斥量加锁,如果拿不到所,则返回false,拿到就返回true

4)release()

  • 该成员函数实际是返回他所管理的mutex对象指针,并释放所有权,也就是说,这个unique_lock和mutex不再有关系

  • 注意事项:release和unlock不要有混淆

void messageIn() {
	for (int i = 0; i < 10000; i++)
	{			
		unique_lock<mutex>guard(myMutex);
		mutex*ptr = guard.release();	
		cout << "messageIn(),服务器收集数据数量:" << i << endl;
		num.push_back(i);
		ptr->unlock();
		}
		return;
	}
  • unique_lockguard(myMutex);这行代码相当于guard和myMutex绑定在一起,release()就是解绑,返回它所管理的mutex对象的指针,并释放所有权,

  • mutex*ptr =
    guard.release();所有权归ptr接管,如果原来mutex对象处理加锁状态,就需要ptr在以后进行解锁了。

  • 深入与提升:

  • 锁头锁住的代码多少称为锁的粒度,粒度一般用粗细来描述

  • 1、锁住的代码少,粒度细,执行效率高

  • 2、锁住的代码多,粒度粗,执行效率低


(4)unique_lock所有权的传递

unique_lockguard(myMutex);所有权概念解释:

  • guard拥有mymutex的所有权
  • guard可以把自己对mutex(mymutex)的所有权转移给其他的unique_lock对象
  • 所有权是可以转移但不能复制
unique_lock<mutex>guard(myMutex);		
unique_lock<mutex>guard2(myMutex);//虽然编译通过但执行错误的
//unique_lockguard2(guard);//复制所有权是非法
unique_lock<mutex>guar2(std::move(guard));
//移动语法。现在相当于guard2和mymutex绑定,guard指向空,guard2指向mymetux

你可能感兴趣的:(c++,多线程,c++11,c++,多线程)