08C++11多线程编程之unique_lock类模板

2023-11-18

08C++11多线程编程之unique_lock类模板

前述:
如果看懂了该篇文章,你对unique_lock可以说随便使用。并且可以只看第5点的总结即可。

1 unique_lock概念
当不加参数时,和lock_guard一样能自动上锁解锁,参数2比lock_guard更加灵活,效率比lock_guard低点,内存也占用大点。看个人喜欢用哪个。
并且和lock_guard都是独占型锁,一把锁只能绑定一个unique_lock,不能多个。类似unique_ptr独占型智能指针。

注:由于不加参数时unique_lock是和lock_guard一样,所以这里就不给出unique_lock无参数2的代码例子。

2 unique_lock的第二个参数
1)std::adopt_lock:表示在创建unique_lock之前已经上锁了。若没上锁,则程序报错崩溃。lock_guard也有唯一的参2->std::adopt_lock,用法完全相同。
这里需要严重的强调一点:adopt_lock不能手动解锁,否则崩溃,与lock_guard的参2->adopt_lock完全一样。与try_to_lock和defer_lock能提前解锁完全不一样。

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

using namespace std;

//准备用成员函数作为线程函数的方法写线程,成为消息处理类
class A {
public:
	//把收到的消息入到一个队列的线程
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
			std::lock(my_mutex1, my_mutex2);//一次锁多个,顺序可以忽略
			unique_lock<mutex> uLock1(my_mutex1, std::adopt_lock);//参数2表示锁已经上锁
			unique_lock<mutex> uLock2(my_mutex2, std::adopt_lock);
			msgRecvQueue.push_back(i);
			//my_mutex1.unlock();//不能再手动解锁,否则崩溃,与lock_guard的参2adopt_lock完全一样
			//my_mutex2.unlock();
		}
	}

	//将共享的代码放到一个函数处理,方便上锁解锁
	bool outMsgLULProc(int &command) {
		std::lock(my_mutex2, my_mutex1);//一次锁多个,顺序可以忽略
		unique_lock<mutex> uLock1(my_mutex1, std::adopt_lock);//参数2表示锁已经上锁
		unique_lock<mutex> uLock2(my_mutex2, std::adopt_lock);
		if (!msgRecvQueue.empty()) {
			int command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			//my_mutex1.unlock();//不能再手动解锁,否则崩溃,与lock_guard的参2adopt_lock完全一样
			//my_mutex2.unlock();
			return true;
		}
		//my_mutex2.unlock();
		//my_mutex1.unlock();
		return false;
	}

	//把数据从消息队列取出的线程
	void outMsgRecvQueue() {
		int command = 0;
		for (int i = 0; i < 10000; i++) {
			bool result = outMsgLULProc(command);
			if (result == true) {
				cout << "outMsgRecvQueue()执行,取出一个元素" << endl;
				//处理数据
				//这里有可能也有需要共享的代码段
			}
			else {
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空!" << endl;
			}
		}
		cout << "end!" << endl;
	}

private:
	std::list<int> msgRecvQueue;//容器(消息队列),代表玩家发送过来的命令。
	std::mutex my_mutex1;
	std::mutex my_mutex2;
};

int main()
{
	A myobja;

	std::thread myOutMsgObj(&A::outMsgRecvQueue, &myobja);//第二个参数,地址,才能保证线程里用的是同一个对象
	std::thread myInMsgObj(&A::inMsgRecvQueue, &myobja);

	myOutMsgObj.join();
	myInMsgObj.join();

	cout << "主线程执行!" << endl;

	return 0;
}

该程序结果是稳定运行的,但是我多次运行发现这个类有bug,就是说当outMsg执行10000次后,可能list还会有元素,但是只是测试就不需要管它了。
下图是std::adopt_lock提前解锁崩溃的图:
在这里插入图片描述

我们知道,当一个线程拿到一把锁之后,另一个线程只能阻塞等待该锁的释放。如果拥有该锁的线程执行时间长,另一个线程等待的时间也长,那么我们能不能让它不等待呢?当然可以,这时候try_to_lock就出现了。

2)std::try_to_lock
std::try_to_lock:表示在创建unique_lock之前不能上锁,否则程序报错崩溃。并且会尝试去上锁,若上锁失败也会立即返回,不会阻塞等待。当std::try_to_lock拿到锁后,也可以提前释放锁,但是没拿到锁不能提前释放,会崩溃。

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

using namespace std;

//准备用成员函数作为线程函数的方法写线程,成为消息处理类
class A{
public:
	//把收到的消息入到一个队列的线程
	void inMsgRecvQueue(){
		for (int i = 0; i < 10000; i++){
			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
			//my_mutex1.lock();//不能先上锁,否则程序崩溃
			unique_lock<mutex> uLock1(my_mutex1, std::try_to_lock);
			if (uLock1.owns_lock()) {
				//拿到了锁
				msgRecvQueue.push_back(i);
				//uLock1.unlock();//ok,拿到锁可以提前释放,然后可以处理其它非共享代码
				//处理其他非共享代码

			}
			else {
				//没拿到锁
				cout << "inMsgRecvQueue()执行,但没拿到锁头,只能干点别的事" << i << endl;
				//uLock1.unlock();//error,没拿到锁不能释放
			}
			
		}
	}

	//将共享的代码放到一个函数处理,方便上锁解锁
	bool outMsgLULProc(int &command){
		unique_lock<mutex> uLock1(my_mutex1, std::try_to_lock);
		if (uLock1.owns_lock()) {
			if (!msgRecvQueue.empty()) {
				int command = msgRecvQueue.front();
				msgRecvQueue.pop_front();
				//uLock1.unlock();//ok,拿到锁可以提前释放,然后可以处理其它非共享代码
				return true;
			}
		}
		else {
			//没拿到锁
			cout << "outMsgLULProc()执行,但没拿到锁头,只能干点别的事" << endl;
		}

		return false;
	}

	//把数据从消息队列取出的线程
	void outMsgRecvQueue(){
		int command = 0;
		for (int i = 0; i < 10000; i++){
			bool result = outMsgLULProc(command);
			if (result == true){
				cout << "outMsgRecvQueue()执行,取出一个元素" << endl;
				//处理数据
				//这里有可能也有需要共享的代码段
			}
			else{
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空!" << endl;
			}
		}
		cout << "end!" << endl;
	}

private:
	std::list<int> msgRecvQueue;//容器(消息队列),代表玩家发送过来的命令。
	std::mutex my_mutex1;
	std::mutex my_mutex2;
};

int main()
{
	A myobja;

	std::thread myOutMsgObj(&A::outMsgRecvQueue, &myobja);//第二个参数,地址,才能保证线程里用的是同一个对象
	std::thread myInMsgObj(&A::inMsgRecvQueue, &myobja);

	myOutMsgObj.join();
	myInMsgObj.join();

	cout << "主线程执行!" << endl;

	return 0;
}

可以看到结果,当一个程序拿到锁后,另一个程序也可以不阻塞,可以去干其它事情。
在这里插入图片描述

3)std::defer_lock
std::defer_lock:表示在创建unique_lock之前不能上锁,否则程序报错崩溃。
同样在拿到锁之后,可以提前释放锁,然后处理非共享代码,提高程序效率。这种也是本人推荐也用得最多的一种。

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

using namespace std;

//准备用成员函数作为线程函数的方法写线程,成为消息处理类
class A{
public:
	//把收到的消息入到一个队列的线程
	void inMsgRecvQueue(){
		for (int i = 0; i < 10000; i++){
			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
			//my_mutex1.lock();//不能先上锁,否则程序崩溃
			unique_lock<mutex> uLock1(my_mutex1, std::defer_lock);
			uLock1.lock();//需要自己上锁
			msgRecvQueue.push_back(i);
			//uLock1.unlock();//ok,拿到锁可以提前释放,然后可以处理其它非共享代码
		}
	}

	//将共享的代码放到一个函数处理,方便上锁解锁
	bool outMsgLULProc(int &command){
		unique_lock<mutex> uLock1(my_mutex1, std::defer_lock);
		uLock1.lock();//需要自己上锁
		if (!msgRecvQueue.empty()) {
			int command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			return true;
		}
		//uLock1.unlock();//ok,拿到锁可以提前释放,然后可以处理其它非共享代码
		return false;
	}

	//把数据从消息队列取出的线程
	void outMsgRecvQueue(){
		int command = 0;
		for (int i = 0; i < 10000; i++){
			bool result = outMsgLULProc(command);
			if (result == true){
				cout << "outMsgRecvQueue()执行,取出一个元素" << endl;
				//处理数据
				//这里有可能也有需要共享的代码段
			}
			else{
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空!" << endl;
			}
		}
		cout << "end!" << endl;
	}

private:
	std::list<int> msgRecvQueue;//容器(消息队列),代表玩家发送过来的命令。
	std::mutex my_mutex1;
	std::mutex my_mutex2;
};

int main()
{
	A myobja;

	std::thread myOutMsgObj(&A::outMsgRecvQueue, &myobja);//第二个参数,地址,才能保证线程里用的是同一个对象
	std::thread myInMsgObj(&A::inMsgRecvQueue, &myobja);

	myOutMsgObj.join();
	myInMsgObj.join();

	cout << "主线程执行!" << endl;

	return 0;
}

3 unique_lock的成员函数
实际上再讲unique_lock的参2的std::try_to_lock、std::defer_lock时已经用到了unique_lock的两个成员函数uLock1.owns_lock()与uLock1.lock()了。

1)lock(),加锁。例子看上面的std::defer_lock即可。
2)unlock(),解锁。例子同样可以看上面的std::defer_lock或者std::try_to_lock即可。
为什么unique_lock可以自动解锁还是提供手动解锁的方式呢,这是因为我们处理完某段共享代码后,可能会出现非共享代码,这时我们解锁后能提高效率。然后再出现共享代码的话我们再次上锁即可。

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

using namespace std;

//准备用成员函数作为线程函数的方法写线程,成为消息处理类
class A {
public:
	//把收到的消息入到一个队列的线程
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
			unique_lock<mutex> uLock1(my_mutex1, std::defer_lock);
			//处理共享代码
			uLock1.lock();
			msgRecvQueue.push_back(i);
			uLock1.unlock();

			//处理完上面的共享代码后,可能出现非共享代码
			//非共享代码

			//共享代码
			uLock1.lock();
			uLock1.unlock();//这句可以不要,让其自动解锁,注意不要写成my_mutex1.unlock()
		}
	}

	//将共享的代码放到一个函数处理,方便上锁解锁
	bool outMsgLULProc(int &command) {
		unique_lock<mutex> uLock1(my_mutex1, std::defer_lock);
		uLock1.lock();
		if (!msgRecvQueue.empty()) {
			int command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			//uLock1.unlock()//这句可以不要,让其自动解锁
			return true;
		}
		//uLock1.unlock();//这句可以不要,让其自动解锁
		return false;
	}

	//把数据从消息队列取出的线程
	void outMsgRecvQueue() {
		int command = 0;
		for (int i = 0; i < 10000; i++) {
			bool result = outMsgLULProc(command);
			if (result == true) {
				cout << "outMsgRecvQueue()执行,取出一个元素" << endl;
				//处理数据
				//这里有可能也有需要共享的代码段
			}
			else {
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空!" << endl;
			}
		}
		cout << "end!" << endl;
	}

private:
	std::list<int> msgRecvQueue;//容器(消息队列),代表玩家发送过来的命令。
	std::mutex my_mutex1;
	std::mutex my_mutex2;
};

int main()
{
	A myobja;

	std::thread myOutMsgObj(&A::outMsgRecvQueue, &myobja);//第二个参数,地址,才能保证线程里用的是同一个对象
	std::thread myInMsgObj(&A::inMsgRecvQueue, &myobja);

	myOutMsgObj.join();
	myInMsgObj.join();

	cout << "主线程执行!" << endl;

	return 0;
}

3)try_lock()
作用:成功上锁返回true,没拿到锁返回false。
该成员函数可以说与constexpr常量宏类型std::try_to_lock(实际为结构体)用法是一样的,同样可以不阻塞和提前解锁。所以我们可以看到,只要使用std::defer常量宏,就基本能使用C++11锁的所有功能了。

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

using namespace std;

//准备用成员函数作为线程函数的方法写线程,成为消息处理类
class A {
public:
	//把收到的消息入到一个队列的线程
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
			unique_lock<mutex> uLock1(my_mutex1, std::defer_lock);
			if (uLock1.try_lock()) {
				msgRecvQueue.push_back(i);
				uLock1.unlock();//ok,上锁成功可以提前解锁
			}
			else {
				cout << "没上锁成功,干点别的事情" << endl;
				//uLock1.unlock();//error,没上锁不能解锁
			}
		}
	}

	//将共享的代码放到一个函数处理,方便上锁解锁
	bool outMsgLULProc(int &command) {
		unique_lock<mutex> uLock1(my_mutex1, std::defer_lock);
		uLock1.lock();
		if (!msgRecvQueue.empty()) {
			int command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			//uLock1.unlock()//这句可以不要,让其自动解锁
			return true;
		}
		//uLock1.unlock();//这句可以不要,让其自动解锁
		return false;
	}

	//把数据从消息队列取出的线程
	void outMsgRecvQueue() {
		int command = 0;
		for (int i = 0; i < 10000; i++) {
			bool result = outMsgLULProc(command);
			if (result == true) {
				cout << "outMsgRecvQueue()执行,取出一个元素" << endl;
				//处理数据
				//这里有可能也有需要共享的代码段
			}
			else {
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空!" << endl;
			}
		}
		cout << "end!" << endl;
	}

private:
	std::list<int> msgRecvQueue;//容器(消息队列),代表玩家发送过来的命令。
	std::mutex my_mutex1;
	std::mutex my_mutex2;
};

int main(){
	A myobja;

	std::thread myOutMsgObj(&A::outMsgRecvQueue, &myobja);//第二个参数,地址,才能保证线程里用的是同一个对象
	std::thread myInMsgObj(&A::inMsgRecvQueue, &myobja);

	myOutMsgObj.join();
	myInMsgObj.join();

	cout << "主线程执行!" << endl;

	return 0;
}

结果:可以看到,都是能稳定运行的,和std::try_to_lock常量宏一样,不会阻塞并且都是能提前解锁的,或者说,只要是std::defer常量宏,都能够提前解锁。
在这里插入图片描述

4)release()成员函数
release()的作用:

  • 1)返回它所管理的mutex对象指针,并释放所有权;也就是说,这个unique_lock和mutex不再有关系。严格区分unlock()与release()的区别,不要混淆。
  • 2)如果原来mutex对像处于加锁状态,你有责任接管过来并负责解锁。也就是说,类似移动语义将该对象交给你,原来什么状态的就处于什么状态。

代码案例讲解release成员函数。为了方便,我们将try_lock()成员函数的代码例子中,只修改inMsg。

//注:以下代码都是正确的。
/*
	1 接管的是上锁状态的锁
*/
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
			unique_lock<mutex> uLock1(my_mutex1);//已经上锁
			mutex *tmpMyx = uLock1.release();//接管已上锁的mutex,移动语义,可自行断点查看地址
			msgRecvQueue.push_back(i);
			tmpMyx->unlock();//需要自行解锁,否则程序出现崩溃
		}
	}

	//或者可以写成这样
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
			my_mutex1.lock();
			unique_lock<mutex> uLock1(my_mutex1, std::adopt_lock);//已经上锁
			mutex *tmpMyx = uLock1.release();//接管已上锁的mutex,移动语义,可自行断点查看地址
			msgRecvQueue.push_back(i);
			tmpMyx->unlock();//需要自行解锁,否则程序出现崩溃
		}
	}

/*
	2 接管的是未上锁状态的锁
*/
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
			unique_lock<mutex> uLock1(my_mutex1, std::defer_lock);
			mutex *tmpMyx = uLock1.release();//接管未上锁的mutex,自行上锁
			tmpMyx->lock();
			msgRecvQueue.push_back(i);
			tmpMyx->unlock();//需要自行解锁,否则程序出现崩溃
		}
	}

仍需注意一点:调用release必须手动上锁解锁,否则容易出现崩溃。
下图是上锁后忘记解锁的崩溃图。
在这里插入图片描述

下图是release接管未上锁的锁后,上锁后也没解锁的崩溃图。
在这里插入图片描述

4 unique_lock所有权的传递
因为unique_lock和lock_guard、unique_ptr都一样,为独占型,要想传递,必须使用移动语义或者返回匿名对象等操作(一般只有这两种就足够了)传递所有权。

std::unique_lock<std::mutex> uLock1(my_mutex);//uLock1拥有my_mutex的所有权;uLock1可以把自己对my_mutex的所有权转移给其他的unique_lock对象;所以unique_lock对象这个mutex的所有权是可以转移,但是不能复制。
//例如
std::unique_lock<std::mutex> uLock2(uLock1);//此句是非法的,复制所有权是非法的

//正确写法:
//1)利用移动语义
void inMsgRecvQueue() {
	for (int i = 0; i < 10000; i++) {
		cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
		unique_lock<mutex> uLock1(my_mutex1);
		unique_lock<mutex> uLock2(std::move(uLock1));//移动语义,现在uLock1指向空,uLock2指向了my_mutex

		msgRecvQueue.push_back(i);
		//uLock2.unlock();//ok,当unique_lock不加参2也可以提前解锁
	}
}

//2)利用返回的匿名对象
std::unique_lock<std::mutex> rtn_unique_lock() {
	std::unique_lock<std::mutex> tmp(my_mutex1);
	return tmp;//注意并非返回tmp,而是返回tmp的复制品,也叫匿名对象
}

//把收到的消息入到一个队列的线程
void inMsgRecvQueue() {
	for (int i = 0; i < 10000; i++) {
		cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
		//unique_lock<mutex> uLock1(my_mutex1);
		//unique_lock<mutex> uLock2(std::move(uLock1));
		unique_lock<mutex> uLock2(rtn_unique_lock());//这样就将tmp中的所有权返回给uLock2,即返回tmp的unique_lock类型的匿名对象,并调用unique_lock的移动构造函数给uLock2赋值
		//unique_lock<mutex> uLock2 = rtn_unique_lock();//或者这样也是和上一句一样
		msgRecvQueue.push_back(i);
		//uLock2.unlock();//ok,当unique_lock不加参2也可以提前解锁
	}
}

可以看到结果,代码都是稳定执行的。
在这里插入图片描述

5 总结unique_lock所有内容

  • 1)当unique_lock不加任何参数时,与lock_guard不加参数一模一样,并且可以提前手动释放锁。
  • 2)unique_lock第二个参数std::adopt_lock,表示在创建unique_lock之前已经上锁了。若没上锁,则程序报错崩溃。lock_guard也有唯一的参2->std::adopt_lock,用法完全相同。并且只有该常量宏是不能手动提前解锁,否则崩溃,与lock_guard的参2->adopt_lock完全一样。与try_to_lock和defer_lock能提前解锁完全不一样。
  • 3)unique_lock第二个参数std::try_to_lock,表示在创建unique_lock之前不能上锁,否则程序报错崩溃。并且会尝试去上锁,若上锁失败也会立即返回,不会阻塞等待。当std::try_to_lock拿到锁后,也可以提前释放锁,但是没拿到锁不能提前释放,会崩溃。
  • 4)unique_lock第二个参数std::defer_lock,表示在创建unique_lock之前不能上锁,否则程序报错崩溃。同样在拿到锁之后,可以提前释放锁,然后处理非共享代码,提高程序效率。这种也是本人推荐也用得最多的一种。它几乎可以使用C++11所有的锁功能。
  • 5)unique_lock的成员函数:lock(),加锁。
  • 6)unique_lock的成员函数:unlock(),解锁。为什么unique_lock可以自动解锁还是提供手动解锁的方式呢,这是因为我们处理完某段共享代码后,可能会出现非共享代码,这时我们解锁后能提高效率。然后再出现共享代码的话我们再次上锁即可。
  • 7)unique_lock的成员函数:try_lock(),作用:成功上锁返回true,没拿到锁返回false。该成员函数可以说与constexpr常量宏类型std::try_to_lock(实际为结构体)用法是一样的,同样可以不阻塞和提前解锁。所以我们可以看到,只要使用std::defer常量宏,就基本能使用C++11锁的所有功能了。
  • 8)unique_lock的成员函数:release(),作用:1)返回它所管理的mutex对象指针,并释放所有权;也就是说,这个unique_lock和mutex不再有关系。严格区分unlock()与release()的区别,不要混淆。2)如果原来mutex对像处于加锁状态,你有责任接管过来并负责解锁。也就是说,类似移动语义将该对象交给你,原来什么状态的就处于什么状态。仍需注意一点:调用release必须手动上锁解锁,凡是没上锁或者忘记解锁,都会出现代码不稳定甚至出现崩溃(大部分情况)。
  • 9)unique_lock所有权的传递:1)利用移动语义。2)利用匿名对象将函数内的局部对象所有权调用移动语义传出外部。并且都可以提前释放锁。再强调一点:只有std::adopt_lock宏是无法提前释放锁的。
  • 10)个人建议:开发时我们只需要用unique_lock的std::defer_lock宏即可,或者直接使用lock_guard,已经完全足够我们使用各个方面。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

08C++11多线程编程之unique_lock类模板 的相关文章

  • 如何在MVVM中管理多个窗口

    我知道有几个与此类似的问题 但我还没有找到明确的答案 我正在尝试深入研究 MVVM 并尽可能保持纯粹 但不确定如何在坚持模式的同时启动 关闭窗口 我最初的想法是向 ViewModel 发送数据绑定命令 触发代码来启动一个新视图 然后通过 X
  • 检查两个数是否是彼此的排列?

    给定两个数字 a b 使得 1 例如 123 是 312 的有效排列 我也不想对数字中的数字进行排序 如果您指的是数字的字符 例如 1927 和 9721 则 至少 有几种方法 如果允许排序 一种方法是简单地sprintf将它们放入两个缓冲
  • 是否可以强制 XMLWriter 将元素写入单引号中?

    这是我的代码 var ptFirstName tboxFirstName Text writer WriteAttributeString first ptFirstName 请注意 即使我使用 ptFirstName 也会以双引号结束 p
  • Qt-Qlist 检查包含自定义类

    有没有办法覆盖加载自定义类的 Qt QList 的比较机制 即在 java 中你只需要重写一个比较方法 我有一个带有我的自定义类模型的 QList QList
  • 如何使用GDB修改内存内容?

    我知道我们可以使用几个命令来访问和读取内存 例如 print p x 但是如何更改任何特定位置的内存内容 在 GDB 中调试时 最简单的是设置程序变量 参见GDB 分配 http sourceware org gdb current onl
  • 将数组向左或向右旋转一定数量的位置,复杂度为 o(n)

    我想编写一个程序 根据用户的输入 正 gt 负 include
  • UML类图:抽象方法和属性是这样写的吗?

    当我第一次为一个小型 C 项目创建 uml 类图时 我在属性方面遇到了一些麻烦 最后我只是将属性添加为变量 lt
  • 从父类调用子类方法

    a doStuff 方法是否可以在不编辑 A 类的情况下打印 B did stuff 如果是这样 我该怎么做 class Program static void Main string args A a new A B b new B a
  • linux perf:如何解释和查找热点

    我尝试了linux perf https perf wiki kernel org index php Main Page今天很实用 但在解释其结果时遇到了困难 我习惯了 valgrind 的 callgrind 这当然是与基于采样的 pe
  • 为什么#pragma optimize("", off)

    我正在审查一个 C MFC 项目 在某些文件的开头有这样一行 pragma optimize off 我知道这会关闭所有以下功能的优化 但这样做的动机通常是什么 我专门使用它来在一组特定代码中获得更好的调试信息 并在优化的情况下编译应用程序
  • 获取没有非标准端口的原始 url (C#)

    第一个问题 环境 MVC C AppHarbor Problem 我正在调用 openid 提供商 并根据域生成绝对回调 url 在我的本地机器上 如果我点击的话 效果很好http localhost 12345 login Request
  • 如何将图像路径保存到Live Tile的WP8本地文件夹

    我正在更新我的 Windows Phone 应用程序以使用新的 WP8 文件存储 API 本地文件夹 而不是 WP7 API 隔离存储文件 旧的工作方法 这是我如何成功地将图像保存到 共享 ShellContent文件夹使用隔离存储文件方法
  • vector 超出范围后不清除内存

    我遇到了以下问题 我不确定我是否错了或者它是一个非常奇怪的错误 我填充了一个巨大的字符串数组 并希望在某个点将其清除 这是一个最小的例子 include
  • 从路径中获取文件夹名称

    我有一些路c server folderName1 another name something another folder 我如何从那里提取最后一个文件夹名称 我尝试了几件事 但没有成功 我只是不想寻找最后的 然后就去休息了 Thank
  • 将自定义元数据添加到 jpeg 文件

    我正在开发一个图像处理项目 C 我需要在处理完成后将自定义元数据写入 jpeg 文件 我怎样才能做到这一点 有没有可用的图书馆可以做到这一点 如果您正在谈论 EXIF 元数据 您可能需要查看exiv2 http www exiv2 org
  • 如何将单个 char 转换为 int [重复]

    这个问题在这里已经有答案了 我有一串数字 例如 123456789 我需要提取它们中的每一个以在计算中使用它们 我当然可以通过索引访问每个字符 但是如何将其转换为 int 我研究过 atoi 但它需要一个字符串作为参数 因此 我必须将每个字
  • Process.Start 阻塞

    我正在调用 Process Start 但它会阻止当前线程 pInfo new ProcessStartInfo C Windows notepad exe Start process mProcess new Process mProce
  • ASP.NET MVC 6 (ASP.NET 5) 中的 Application_PreSendRequestHeaders 和 Application_BeginRequest

    如何在 ASP NET 5 MVC6 中使用这些方法 在 MVC5 中 我在 Global asax 中使用了它 现在呢 也许是入门班 protected void Application PreSendRequestHeaders obj
  • C 中的异或运算符

    在进行按位操作时 我在确定何时使用 XOR 运算符时遇到一些困难 按位与和或非常简单 当您想要屏蔽位时 请使用按位 AND 常见用例是 IP 寻址和子网掩码 当您想要打开位时 请使用包含或 然而 XOR 总是让我明白 我觉得如果在面试中被问
  • 如何在 C++ BOOST 中像图形一样加载 TIFF 图像

    我想要加载一个 tiff 图像 带有带有浮点值的像素的 GEOTIFF 例如 boost C 中的图形 我是 C 的新手 我的目标是使用从源 A 到目标 B 的双向 Dijkstra 来获得更高的性能 Boost GIL load tiif

随机推荐

  • React Native

    小手动一动 点赞转发加关注 微信搜索 大前端杂货铺 公众号关注大前端老司机带您遨游大前端知识的海洋 关注 Github https github com big frontend 还有大前端代码实践哦 java 与 javascript 互
  • C++基础——简单而强大的bitset

    basis bitset 的构造 bitset的操作 一些高级用法 将Bitsets视为一组标志 一些简单的原子操作 往往能组合出复杂而强大的功能 位操作的深远意义不在于表示一种数值 而是可能的情况数 我虽然暂时不知道bitset能组合出如
  • Python 之列表添加元素的3种方法

    一 追加单个值 append 方法 追加单个元素 gt gt gt list crazyit 20 2 gt gt gt list append fkit gt gt gt print list crazyit 20 2 fkit 二 追加
  • Configure and build Mesa3D

    1 环境 Mesa3D 21 1 4 Mesa3D demos Ubuntu 20 04 2 配置环境 sudo apt install gcc sudo apt install g sudo apt install vim sudo ap
  • React-Native ERROR: JAVA_HOME is not set and no ‘java’ command could be found in your PATH.

    ERROR JAVA HOME is not set and no java command could be found in your PATH Please set the JAVA HOME variable in your env
  • React重点知识拓展,含Hooks、路由懒加载等

    第7章 React扩展 一 setState 1 setState更新状态的2种写法 setState stateChange callback 对象式的setState stateChange为状态改变的对象 该对象可以体现出状态的更改
  • 华为云原生之数据仓库服务GaussDB(DWS)的深度使用与应用实践

    一 GaussDB DWS 简介 什么是 GaussDB DWS 数据仓库服务 GaussDB DWS 是一种基于华为云基础架构和平台的在线数据处理数据库 提供即开即用 可扩展且完全托管的分析型数据库服务 GaussDB DWS 是基于华为
  • java内存工具VisualVM的简单使用以及与Idea集成

    一 idea集成 1 打开设置 windows File gt Setting MacOS Intelij Idea gt Preferences 1 2 打开插件仓库 Plugins gt Browers Repositrories 在这
  • SPI总线协议概述

    一 概述 SPI serial peripheral interface 是一种同步串行通信协议 由一个主设备和一个或多个从设备组成 主设备启动与从设备的同步通信 从而完成数据的交换 SPI是一种高速全双工同步通信总线 标准的SPI仅仅使用
  • 【Python】一个矩阵根据某一列选择大于或小于范围的数据

    data all data all data all 3 gt 54201 data all data all data all 3 lt 54220 上面就是根据数据的第3列 选取54201到54220的范围的数据
  • AD20的元件库及加载(一)

    开始时我们画的是元器件 元器件在后缀名为 的文件里画 以画电容为例 上图就是结果 画着简单 过程能学会很多 小伙伴们可能刚刚接触 不知道在哪找线 选择元器件不放 按住鼠标左键 并按下空格 即可旋转元器件的角度 每次旋转90度 电阻的两边的四
  • 福到了(15 分)

    L1 6 福到了 15 分 福 字倒着贴 寓意 福到 不论到底算不算民俗 本题且请你编写程序 把各种汉字倒过来输出 这里要处理的每个汉字是由一个 N N 的网格组成的 网格中的元素或者为字符 或者为空格 而倒过来的汉字所用的字符由裁判指定
  • 软件测试基础内容学习(二)之边界值分析法

    对限定边界规则设计测试点 边界值分析法 说明 1 边界范围节点 2 应用设计步骤 3 案例 4 适用场景 边界范围节点 选取正好等于 上点 刚好大于 刚好小于 离点 边界的值作为测试数据 内点 一般取最中间的点 在范围内的点 应用设计步骤
  • Python deque的用法介绍

    Python deque的用法介绍 deque 是Python标准库 collections 中的一个类 实现了两端都可以操作的队列 相当于双端队列 与Python的基本数据类型列表很相似 Python实现双端队列参考 https blog
  • JVM 一. 字节码与数据类型相关

    目录 一 字节码 数据类型相关 一 字节码 数据类型相关 字节码文件是跨平台的吗 是的 java虚拟机主要识别字节码文件 其实现在的java虚拟机已经不是单纯的java的 只要语言满足虚拟机的规范 都可以在这个虚拟机上运行 class文件中
  • 一、Kubernetes系列之介绍篇

    Kubernetes介绍 1 背景介绍 云计算飞速发展 IaaS PaaS SaaS Docker技术突飞猛进 一次构建 到处运行 容器的快速轻量 完整的生态环境 2 什么是kubernetes 首先 他是一个 全新的基于容器技术的分布式架
  • 微信回调模式配置企业服务器URL

    转载请标明出处 尊重他人劳动成果 谢谢 前几天微信推出了企业号 我就进去关注了一下 发现用途大大的多 就顺手整了一个测试号来试试 由于是新出的玩意儿 很多东西有文档也不到一定知道整 我这个配置就花了蛮久才找到失败的原因 最终是借用了浩然哥的
  • STL——queue模板类常见函数

    include
  • 【Python数据分析】Pandas按行遍历Dataframe

    Pandas按行遍历Dataframe的方法主要有两种 iterrows 和itertuples 具体用法如下 构建数据集 import pandas as pd import numpy as np N 20 dataset pd Dat
  • 08C++11多线程编程之unique_lock类模板

    08C 11多线程编程之unique lock类模板 前述 如果看懂了该篇文章 你对unique lock可以说随便使用 并且可以只看第5点的总结即可 1 unique lock概念 当不加参数时 和lock guard一样能自动上锁解锁