52 C++ unique_lock 替代 lock_guard 详解

一  前提,lock_guard 详解:

lock_guard是 个类模版,不同的是 lock_guard 的这个模版只能,

lock_guard mylock_guard(mymutex);

52 C++ unique_lock 替代 lock_guard 详解_第1张图片

可以看到,lock_guard在实例化对象的时候,有两种构造方法。

我们这里看有两个参数的构造方法,也就是第二个参数 是std::adopt_lock_t的构造方法。

std::adopt_lock_t  

假设调用方线程已拥有互斥的所有权

struct adopt_lock_t { explicit adopt_lock_t() = default; };

从C++的文档中可以看出,这玩意是个 struct,实际上,理解为标志位更为合适。

adopt_lock_t  意思是:我们在lock_guard构造函数中不调用 mutex1.lock(),只是在析构函数中调用 mutex1.unlock();

mymutex1.lock()
lock_guard sbguard1(mymutex1,adopt_lock);

二  unique_lock 详解

unique_lock 也是 类模版,只能使用,

unique_lock的构造函数比lock_guard的构造函数多了几个:当前我们研究一下第二个参数是 ;

adopt_lock 意思是:调用这一行之前,一定是有lock()过的,我们在lock_guard构造函数中不调用 mutex1.lock(),只是在析构函数中调用 mutex1.unlock();

mymutex1.lock()
unique_lock sbguard1(mymutex1,adopt_lock);

try_to_lock 意思是:我们会尝试用mutex的lock()去锁定这个mutex,但是如果没有锁定成功,我也会立即返回,并不会阻塞在这里。那么怎么知道锁定成功了吗?

owns_lock()方法说明:检查 *this 是否占有锁定的互斥。

例子:

mymutex1.lock()
unique_lock sbguard1(mymutex1,try_to_lock);

if(sbguard1.owns_lock()){

        //如果拿到了锁子的逻辑

} else{

        //没拿到锁子的逻辑

}

defer_lock 意思是:并没有给mutex加锁:初始化一个没有加锁的mutex,那么不能给mutex加锁,这个有啥用?

-------实际上要结合 unique_lock 的成员函数来能理解为什么要这么干。

lock()函数

好处是不用管unlock(),在 sbguard1的析构函数调用的时候,会调用 mymutex1的析构函数

            unique_lock sbguard1(mymutex1, defer_lock);
            unique_lock sbguard2(mymutex2, defer_lock);
            sbguard1.lock();
            sbguard2.lock();

class Teacher171 {

public:
	//共享数据 存在list中
	list msgListQueue;
	list msgListQueue2;
	mutex mymutex1;
	mutex mymutex2;
	int readcount = 0;//记录已经读取了多少个。
public:
	//读取 共享数据的线程方法
	void readfunc() {
		while (true) {
			//只要不为空,就可以读取数据
			if (readcount >= 2000) {
				break;
			}

			//fix方案3.使用lock(mutex1,mutex2); 参数都是mutex,代表的就是顺序,因此保证每个地方的调用都是一样的就可以了
			// 这里使用 lock_guard sbguard1(mymutex1,adopt_lock);的含义是:在lock_guard构造函数中不调用 mutex1.lock(),只是在析构函数中调用 mutex1.unlock();
			lock(mymutex1, mymutex2);
			lock_guard sbguard1(mymutex1, adopt_lock);
			lock_guard sbguard2(mymutex2, adopt_lock);

			if (!msgListQueue.empty()) {
				int readvalue = msgListQueue.front();//每次都读取第一个
				cout << "读取到的值为" << readvalue << " readcount = " << readcount << endl;
				msgListQueue.pop_front();//删除第一个元素
				readcount++;
			}
			else {
				cout << "没有读取到值" << endl;
			}

			if (!msgListQueue2.empty()) {
				int readvalue = msgListQueue2.front();//每次都读取第一个
				cout << "222读取到的值为" << readvalue << " readcount = " << readcount << endl;
				msgListQueue2.pop_front();//删除第一个元素
				readcount++;
			}
			else {
				cout << "222没有读取到值" << endl;
			}
		}
	}

	//写入 共享数据的线程方法
	void writefunc() {
		for (size_t i = 0; i < 1000; i++)
		{
			unique_lock sbguard1(mymutex1, defer_lock);
			unique_lock sbguard2(mymutex2, defer_lock);
			sbguard1.lock();
			sbguard2.lock();
			msgListQueue.push_back(i);//每次都写到末尾
			cout << "写入元素的值为" << i << endl;
		}
	}

public:
	Teacher171() {
		cout << "Teacher171 构造方法 this = " << this << endl;
	}

	Teacher171(const Teacher171 & obj) {
		cout << "Teacher171 copy 构造方法 this = " << this << "  obj = " << &obj << endl;
	}

	~Teacher171() {
		cout << "Teacher171 析构函数 this = " << this << endl;
	}
};


void main() {

	cout << "=========================" << endl;

	Teacher171 tea1;
	thread readthread1(&Teacher171::readfunc, &tea1);
	thread writethread1(&Teacher171::writefunc, &tea1);
	thread readthread2(&Teacher171::readfunc, &tea1);
	thread writethread2(&Teacher171::writefunc, &tea1);
	readthread1.join();
	writethread1.join();
	readthread2.join();
	writethread2.join();
}

unlock()函数

之前的lock_guard是要在析构函数调用的时候,才会调用 mutex.unlock();

这有时候不方便,因此提供了unlock()方法

class Teacher171 {

public:
	//共享数据 存在list中
	list msgListQueue;
	list msgListQueue2;
	mutex mymutex1;
	mutex mymutex2;
	int readcount = 0;//记录已经读取了多少个。
public:
	//读取 共享数据的线程方法
	void readfunc() {
		while (true) {
			//只要不为空,就可以读取数据
			if (readcount >= 2000) {
				break;
			}

			//fix方案3.使用lock(mutex1,mutex2); 参数都是mutex,代表的就是顺序,因此保证每个地方的调用都是一样的就可以了
			// 这里使用 lock_guard sbguard1(mymutex1,adopt_lock);的含义是:在lock_guard构造函数中不调用 mutex1.lock(),只是在析构函数中调用 mutex1.unlock();
			lock(mymutex1, mymutex2);
			lock_guard sbguard1(mymutex1, adopt_lock);
			lock_guard sbguard2(mymutex2, adopt_lock);

			if (!msgListQueue.empty()) {
				int readvalue = msgListQueue.front();//每次都读取第一个
				cout << "读取到的值为" << readvalue << " readcount = " << readcount << endl;
				msgListQueue.pop_front();//删除第一个元素
				readcount++;
			}
			else {
				cout << "没有读取到值" << endl;
			}

			if (!msgListQueue2.empty()) {
				int readvalue = msgListQueue2.front();//每次都读取第一个
				cout << "222读取到的值为" << readvalue << " readcount = " << readcount << endl;
				msgListQueue2.pop_front();//删除第一个元素
				readcount++;
			}
			else {
				cout << "222没有读取到值" << endl;
			}
		}
	}

	//写入 共享数据的线程方法
	void writefunc() {
		for (size_t i = 0; i < 1000; i++)
		{
			unique_lock sbguard1(mymutex1, defer_lock);
			unique_lock sbguard2(mymutex2, defer_lock);
			sbguard1.lock();
			sbguard2.lock();
			msgListQueue.push_back(i);//每次都写到末尾
			cout << "写入元素的值为" << i << endl;
			sbguard1.unlock();
			sbguard2.unlock();
		}
	}

public:
	Teacher171() {
		cout << "Teacher171 构造方法 this = " << this << endl;
	}

	Teacher171(const Teacher171 & obj) {
		cout << "Teacher171 copy 构造方法 this = " << this << "  obj = " << &obj << endl;
	}

	~Teacher171() {
		cout << "Teacher171 析构函数 this = " << this << endl;
	}
};


void main() {

	cout << "=========================" << endl;

	Teacher171 tea1;
	thread readthread1(&Teacher171::readfunc, &tea1);
	thread writethread1(&Teacher171::writefunc, &tea1);
	thread readthread2(&Teacher171::readfunc, &tea1);
	thread writethread2(&Teacher171::writefunc, &tea1);
	readthread1.join();
	writethread1.join();
	readthread2.join();
	writethread2.join();
}

try_lock()函数

我们会尝试用mutex的lock()去锁定这个mutex,但是如果没有锁定成功,我也会立即返回,并不会阻塞在这里。那么怎么知道锁定成功了吗?mymutex1.lock()
unique_lock sbguard1(mymutex1,adopt_lock);

if(sbguard1.owns_lock()){

        //如果拿到了锁子的逻辑

} else{

        //没拿到锁子的逻辑

}

class Teacher171 {

public:
	//共享数据 存在list中
	list msgListQueue;
	mutex mymutex1;
public:
	//读取 共享数据的线程方法
	void readfunc() {

		for (size_t i = 0; i < 10000; ++i) {
			unique_lock sbguard1(mymutex1,defer_lock);
			if (sbguard1.try_lock() == true) {
				if (!msgListQueue.empty()) {
					int readvalue = msgListQueue.front();//每次都读取第一个
					cout << "读到的值为" << readvalue  << endl;
					msgListQueue.pop_front();//删除第一个元素
				}
			}
			else {
				cout << "读取第" << i << "次" << endl;
			}
		}
	}

	//写入 共享数据的线程方法
	void writefunc() {
		for (size_t i = 0; i < 10000; ++i)
		{
			unique_lock sbguard1(mymutex1, defer_lock);
			if (sbguard1.try_lock() == true) {
				msgListQueue.push_back(i);//每次都写到末尾
				cout << "写入的值为" << i << endl;
			}
			else {
				cout << "写入第" << i << "次" << endl;
			}
		}
	}

public:
	Teacher171() {
		cout << "Teacher171 构造方法 this = " << this << endl;
	}

	Teacher171(const Teacher171 & obj) {
		cout << "Teacher171 copy 构造方法 this = " << this << "  obj = " << &obj << endl;
	}

	~Teacher171() {
		cout << "Teacher171 析构函数 this = " << this << endl;
	}
};

void main() {

	cout << "=========================" << endl;
	Teacher171 tea1;
	thread readthread1(&Teacher171::readfunc, &tea1);
	thread writethread1(&Teacher171::writefunc, &tea1);
	readthread1.join();
	writethread1.join();
}

release()函数

返回它所管理的mutex对象指针,并释放所有权,也就是说:这个uique_lock 和mutex不在有关系,如果这时候mutex处理加锁状态,则要程序员自己对这个mutex进行unlock操作。

			unique_lock sbguard1(mymutex1,defer_lock);
			if (sbguard1.try_lock() == true) {
				if (!msgListQueue.empty()) {
					int readvalue = msgListQueue.front();//每次都读取第一个
					cout << "读到的值为" << readvalue  << endl;
					msgListQueue.pop_front();//删除第一个元素
				}
				mutex* ponitmutex =  sbguard1.release();//这时候加上锁子了,我们将sbguard1和它内部的mutex关系解除了,那么下面就要自己对 mutex进行unlock
				ponitmutex->unlock();
			}

unique_lock所有权转移

		unique_lock sbguard1(mymutex1, defer_lock);
		unique_lock sbguard2(move(sbguard1));

C++文档的解释:
//unique_lock( unique_lock&& other ) noexcept; 2) 移动构造函数。以 other 的内容初始化 unique_lock 。令 other 无关联互斥。

意思:
1.sbguard1 和 mymutex1 不在有关系
2.sbguard2 和 mymutex1 关联

52 C++ unique_lock 替代 lock_guard 详解_第2张图片

52 C++ unique_lock 替代 lock_guard 详解_第3张图片

你可能感兴趣的:(c++)