unique_lock详解

unique_lock详解

视频 :https://www.bilibili.com/video/BV1Yb411L7ak?p=8&vd_source=4c026d3f6b5fac18846e94bc649fd7d0
参考文章:https://blog.csdn.net/weixin_42636062/article/details/12006833
img

unique_lock取代lock_guard

  • unique_lock是个类模板,工作中,一般使用lock_guard(推荐使用!)
  • lock_guard取代了mutexlock()unlock();
  • unique_locklock_guard灵活很多,效率上差一点,内存占用多一点。

unique_lock的第二个参数

std::adopt_lock:

std::adopt_lock:表示这个互斥量已经被lock了(你必须提前把互斥量lock,否则会报异常)std::adopt_lock标记的效果是“假设调用方线程已经拥有了互斥的所有权(已经lock()成功了);通知lock_guard不需要在构造函数中lock这个互斥量了

std::try_to_lock

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

#include <iostream>
#include <thread>
#include <vector>
#include <list>
#include <string>
#include <mutex>

using namespace std;


class A {
public:
	//把收到的信息(玩家命令) 排到一个队列的线程
	void inMsgRecvQueue() {
		for (int i = 0; i < 100000; ++i) {
			cout << "inMsgRecvQueue()执行,插入一个元素" << endl;

			std::unique_lock<std::mutex> sbguard1(my_mutex1, std::try_to_lock);
			if (sbguard1.owns_lock()) {
				msgRecvQueue.push_back(i);   //假设这个数字i就是我收到的命令,我直接弄到消息队列里边
			}
			else
			{
				//没拿到锁
				cout << "inMsgRecvQueue()执行,但没拿到锁头,只能干点别的事" << i << endl;
			}
	       /*my_mutex2.unlock();
			my_mutex1.unlock();*/

		}
		return;
	}

	bool outMsgLULProc(int& command) {
		//消息不为空


		std::lock(my_mutex1, my_mutex2);

		std::lock_guard<std::mutex> s1(my_mutex1, std::adopt_lock);
		std::lock_guard<std::mutex> s2(my_mutex2, std::adopt_lock);
		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;
			}
		}

		cout << "end" << endl;
	}

private:
	std::list<int> msgRecvQueue; //容器,专门用来代表玩家给咱们发过来的命令。这里就相当于是共享内存
	std::mutex my_mutex1;//创建一个互斥量
	std::mutex my_mutex2;
};



int main() {
	//准备用成员函数作为线程函数的方法来写线程;
	A mytobj;
	std::thread myOutMsgobj(&A::outMsgRecvQueue, &mytobj);   //第二个参数是引用,这样就不是复制一份了,但是就不能用detach了
	std::thread myInMsgobj(&A::inMsgRecvQueue, &mytobj);

	myOutMsgobj.join();
	myInMsgobj.join();//两个谁先谁后无所谓


	return 0;
}

std::defer_lock

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

defer_lock参数

当使用std::defer_lock参数时,互斥量对象不会立即被加锁,而是延迟到之后的某个时刻再进行加锁操作。

这个特性有时候很有用,特别是当你需要在一段代码中根据条件选择是否需要加锁时。

使用std::defer_lock的一个典型场景是在使用多个互斥量时,需要在获取所有互斥量的锁之后才能继续执行后续的操作。

lock()和unlock()

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;

void workWithMutex() {
    std::unique_lock<std::mutex> lock(mtx, std::defer_lock);  // 使用std::defer_lock参数延迟加锁

    // 在这里可以进行一些无需加锁的操作

    // 根据条件选择加锁
    if(/* 满足某个条件 */) {
        lock.lock();  // 手动加锁
        // 在这里可以安全地访问和修改共享数据
        lock.unlock();  // 手动解锁
    }

    // 继续进行其他操作,无需加锁

    // 在这里可以选择再次加锁来访问和修改共享数据
    lock.lock();  // 手动加锁
    // 在这里可以安全地访问和修改共享数据
    lock.unlock();  // 手动解锁
}

int main() {
    std::thread t(workWithMutex);  // 创建一个线程来执行workWithMutex函数
    t.join();  // 等待线程执行完毕

    return 0;
}

在这个示例中,我们创建了一个互斥量对象mtx,并在workWithMutex函数中使用std::unique_lock来管理互斥量的加锁和解锁操作。

在创建unique_lock对象时,我们传入了std::defer_lock参数,表示互斥量会被延迟加锁,即不会立即加锁。

workWithMutex函数中,我们可以在无需加锁的部分进行一些操作。然后,根据某个条件,我们可以选择手动地加锁和解锁互斥量来安全地访问和修改共享数据。

之后,我们又可以选择再次手动加锁和解锁互斥量,以继续访问和修改共享数据。

希望这次的解释更符合您的需求。如果还有其他问题,请随时提问。

try_lock():尝试给互斥量加锁

如果拿不到锁,返回false,否则返回true。
这里直接借用大佬代码

void inMsgRecvQueue(){
    for (int i = 0; i < 10000; i++)
    {
        std::unique_lock<std::mutex> sbguard(my_mutex, std::defer_lock);//没有加锁的my_mutex

        if (sbguard.try_lock() == true)//返回true表示拿到锁了
        {
            msgRecvQueue.push_back(i);
            //...
            //其他处理代码
        }
        else
        {
            //没拿到锁
            cout << "inMsgRecvQueue()执行,但没拿到锁头,只能干点别的事" << i << endl;
        }
    }
}

release()

这里直接借用博主的话,加了下代码

  • unique_lock
    myUniLock(myMutex);相当于把myMutex和myUniLock绑定在了一起,release()就是解除绑定,返回它所管理的mutex对象的指针,并释放所有权
  • mutex* ptx =
    myUniLock.release();所有权由ptx接管,如果原来mutex对象处理加锁状态,就需要ptx在以后进行解锁了。
void inMsgRecvQueue() {
		for (int i = 0; i < 100000; ++i) {
			cout << "inMsgRecvQueue()执行,插入一个元素" << endl;

			std::unique_lock<std::mutex> sbguard1(my_mutex1);
			std::mutex* ptx = sbguard1.release();//现在你有责任自己解锁mutex1;相当于这个传递就把
			//啥都给了你,不仅要你解锁,连锁着的状态也是传递给你的
			
		   msgRecvQueue.push_back(i);   //假设这个数字i就是我收到的命令,我直接弄到消息队列里边
			
		   ptx->unlock();
	       

		}
		return;
	}

unique_lock所有权的传递 (move())

std::unique_lock<mutex> sbguard(my_mutex1):所有权概念
sbguard1拥有my_mutex1的所有权
但是可以将所有权转移给其他的unique_lock对象,但不是复制
img

unique_lock<mutex> aFunction()
{
    unique_lock<mutex> myUniLock(myMutex);
    //移动构造函数那里讲从函数返回一个局部的unique_lock对象是可以的
    //返回这种局部对象会导致系统生成临时的unique_lock对象,并调用unique_lock的移动构造函数
    return myUniLock;
}
posted @   NoAcalculia  阅读(473)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示