**
前言
**
将之前的一个多线程案例修改一下,增添了运行时间判断的函数
#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]:通过传值方式捕获当前对象;