C++11多线程std::condition_variable与lambda表达式的简单使用

**

前言

**
在这里插入图片描述

将之前的一个多线程案例修改一下,增添了运行时间判断的函数

#include<sys/timeb.h>
//获取执行时间
long getSysTime() {
	struct timeb tb;
	ftime(&tb);
	return tb.time * 1000 + tb.millitm;
}

和双重锁定后:

#include<iostream>
#include<thread>
#include<mutex>
#include<list>
#include<sys/timeb.h>
using namespace std;


//获取执行时间
long getSysTime() {
	struct timeb tb;
	ftime(&tb);
	return tb.time * 1000 + tb.millitm;
}


class A {
private:
	std::list<int> msgRecvQueue;//专门用于代表玩家给我们发的命令 , 共享数据
	std::mutex  myMutex1;

public:
	//线程1 ,把收到的玩家命令dump到一个队列中
	void inMsgRecvQueue() {
		for (int i = 0; i < 100; ++i) {

			std::unique_lock<std::mutex> s1(myMutex1);
			cout << "#inMsgRecvQueue()执行,插入一个元素>>" << i << "\n";
			msgRecvQueue.push_back(i);
		}
		cout << "\n#end inMsgRecvQueue\n";
	}

	void outMsgLULProc(int& command) {

		if (!msgRecvQueue.empty()) {//双重锁定
			std::unique_lock<std::mutex> s1(myMutex1);
			cout << "#outMsgLULProc()执行,取出一个元素>>" << command << "\n";
			if (!msgRecvQueue.empty()) {
				command = msgRecvQueue.front();
				msgRecvQueue.pop_front();
				return ;
			}
		}
		cout << "#消息队列为空\n";
		return ;
	}
	//线程2 , 把命令队列中数据取出
	void outMsgRecvQueue() {
		int command = 0;
		for (int i = 0; i < 100; ++i) {
			outMsgLULProc(command);
		}

		cout << "\n#end outMsgRecvQueue\n";

	}
};



void main() {

	A obj;

	long cd = getSysTime();
	thread out(&A::outMsgRecvQueue, &obj);
	thread in(&A::inMsgRecvQueue, &obj);

	out.join();
	in.join();

	cd = getSysTime() - cd;
	cout << "消耗时间>" << cd << " ms \n";

	system("pause");

}

还有,将输出的代码写在锁内就让打印显得更规整了一些。但是由于两个线程乱序输出,还是无法避免的有打印出错的地方。
在这里插入图片描述
为了解决这个输出不规整的问题,我需要让线程“有序”地运行,即输出的运行完输入的才开始,而不是在一句话没打印完,另一个线程就抢着打印了。
还有,对于双重锁定,还有没有比这种方法更快的?

也许下面这个有点作用。
**

std::condition_variable

**
这是个条件类变量,这个类有三个常用的函数:wait()、notify_one()、notify_all()

- wait()一般有两个参数,第一个一个unique_lock对象,第二个参数是一个返回bool类型的函数。
- 当我使用unique_lock给线程上锁后,等到抢到锁的线程执行到wait()-------------如果函数返回true,或是没有第二个参数,那么这个wait()不起作用。线程继续向下执行。
- 如果函数返回false,那么此线程会解锁,然后等待一个信号,不然一直停留阻塞在这个wait()函数。
- 信号由notify_one()、notify_all()发送,等到信号后,该线程再去抢锁,如果抢到,再判断函数返回-------------如果一直返回false,就死循环了。
-如果没有信号发送过去,wait()的线程会一直死在那

#include<iostream>
#include<thread>
#include<mutex>
#include<list>
#include<sys/timeb.h>
using namespace std;




//获取执行时间
long getSysTime() {
	struct timeb tb;
	ftime(&tb);
	return tb.time * 1000 + tb.millitm;
}

class A {
private:
	std::list<int> msgRecvQueue;//专门用于代表玩家给我们发的命令 , 共享数据
	std::mutex  myMutex1;
	std::condition_variable my_cond;//我的条件变量对象

public:

	void inMsgRecvQueue() {
		for (int i = 0; i < 100; ++i) {
			std::unique_lock<std::mutex> s1(myMutex1);
			cout << "#inMsgRecvQueue()执行,插入一个元素>>" << i << "\n";//输出全部写在锁内部后,就规整多了
			msgRecvQueue.push_back(i);
			my_cond.notify_one();//尝试把wait()的线程唤醒,执行完这行,wait()就醒了

		}
		cout << "\n#end inMsgRecvQueue\n";
	}

	void outMsgRecvQueue() {
	
		int command = 0;
		for (int i = 0; i < 100;++i) {   
			std::unique_lock<std::mutex> s1(myMutex1);
		
			my_cond.wait(s1,
				[this,i](){    
				if (!msgRecvQueue.empty())
					return true;
				cout << "waiting......"<<i<<"\n";
				return false;    
			});//闭包
			
			command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			cout << "#outMsgRecvQueue执行,取出一个元素>>>" << command <<"thread id>"<<std::this_thread::get_id()<< "\n";
			s1.unlock();//提前解锁		 归功于unique_lock的灵活性
			
			//....

		}

		cout << "\n#end outMsgRecvQueue\n";

	}
};




void main() {
	A obj;

	long cd = getSysTime();
	thread out(&A::outMsgRecvQueue, &obj);
	thread in(&A::inMsgRecvQueue, &obj);

	out.join();
	in.join();
	cd = getSysTime() - cd;
	cout << "消耗时间>" << cd << " ms \n";

	system("pause");

}
  • wait()第二个参数可以是lambda表达式,上面是[ ] 捕捉了当前对象和一个变量 i .

上面这个例子中,插入100个同时也需要取出100个,如果把代码改成取出101个,就会出现取出的线程一直卡在wait()处的情况。
当把取出的线程数增加到3个时,循环只能运行33次,即3个线程取出的数个数总和必须小于等于100.

lambda表达式

这是一种闭包,是内嵌函数。
基本格式是:auto a = [ ] { } ;

#include<iostream>
#include<vector>
#include<functional>
#include<algorithm>
using namespace std;




void test01() { 

	//简单的lambda
	auto basicLambda = [] {cout << "hello,world!" << endl; };
	basicLambda();

	//指明返回类型
	auto add = [](int a, int b)->int {return a + b; };
	//自动推断返回类型
	auto multiply = [](int a, int b) {return a * b; };
	cout << "2 + 5 = " << add(2, 5) << "\n2 X 5 = " << multiply(2, 5);

	//泛型lambda
	auto add_2 = [](auto x, auto y) {return x + y; };
	cout <<"\n" <<add_2(2.5, 3.5) << endl;


}
void test02() {//捕捉

	int x = 10;

	//mutable保证捕捉的变量(复制捕捉)可修改
	auto add_x = [x](int a) mutable {  x *= 2;  return a + x; };//复制捕捉x
	//引用捕捉的可以随便修改
	auto multiply_x = [&x](int a) { x *= 2; return a * x; };//引用捕捉x

	cout << "add_x(10) = " << add_x(10) << "\nmultiply_x(10) = " << multiply_x(10) << endl;


}

void test03() {

	auto a = [] {cout << "A" << endl; };
	auto b = [] {cout << "B" << endl; };
	//a = b;非法,lambda无法赋值,毕竟lambda禁用了赋值操作符
	auto c = a;///合法,会生成一股副本,毕竟没有禁用拷贝构造函数

}

}
void  main() {

	test01();
	//test02();

	system("pause");

}

()内写传递的变量
mutable (任何情况下,变量可修改的标识符)写在花括号前 mutable { } ;
[ ] 内需要写捕捉的东西:

  • [ ]:默认不捕获任何变量;
    [=]:默认以值捕获所有变量; [&]:默认以引用捕获所有变量;
    [x]:仅以值捕获x,其它变量不捕获; [&x]:仅以引用捕获x,其它变量不捕获;
    [=, &x]:默认以值捕获所有变量,但是x是例外,通过引用捕获; [&, x]:默认以引用捕获所有变量,但是x是例外,通过值捕获;
    [this]:通过引用捕获当前对象(其实是复制指针); [*this]:通过传值方式捕获当前对象;

猜你喜欢

转载自blog.csdn.net/weixin_41374099/article/details/88324141