头文件
定义 class condition_variable;
简介
之前我们也已经介绍过了C++多线程中互斥变量存在,已经足够支持多线程中对共享普通系统数据的合理访问。但是因为多线程中同一时刻必然会有一个线程持有锁,一个线程等待锁。而在代码中使用while
方式的循环等待必然会导致系统效率降低,cpu被无用消耗。
查看如下代码
#include
#include
#include
#include
#include //包含deque的头文件
using namespace std;std::mutex mu;
std::deque<int> q; //全局共享数据,双端队列void function_1(){ int count = 100;while (count > 0) { std::unique_lock<std::mutex> locker(mu);q.push_back(count);locker.unlock();std::this_thread::sleep_for(std::chrono::seconds(1));count--;}
}void function_2() { int data = 0;while (data != 1) { std::unique_lock<std::mutex> locker(mu);//获取到锁之后检测全局共享队列是否为空,如果不为空则执行出队操作if (!q.empty()) { data=q.back();q.pop_back();locker.unlock();std::cout << "t2 got a value from t1 " << data << std::endl; } else { //如果为空,则尝试解锁locker.unlock();//防止消耗cpu//std::this_thread::sleep_for(std::chrono::seconds(1)); }}
}
int main()
{ std::thread t1(function_1);std::thread t2(function_2);t1.join();t2.join();return 0;
}
由以上代码可以很明显得看出来t1线程提供数据push_back
(生产者),t2线程消费数据pop_back
(消费者),这两个线程最合理得运行方式应该为生产者生产好数据,然后唤醒消费者去消费,消费完成之后继续休眠。
但是在t2线程如果检测到全局队列为空,则会尝试解锁,但是在t1线程向全局队列push数据的时候t2线程仍然在尝试解锁,这样的操作会导致系统cpu资源被无端消耗,即消费者一直在等待着生产者生产。
这个时候我们的条件变量即可出山,它能够比sleep_for
函数使用起来更灵活
在解决以上问题之前,先介绍几个条件变量包含的成员函数
std::condition_variable::notify_one
和 std::condition_variable::notify_one
通知一个等待线程,同时改函数作用在当前条件变量上std::condition_variable::wait()
导致当前线程阻塞直至条件变量被通知wait( std::unique_lock)
函数的实现方式是先原子解锁线程,将当前线程加入到线程的等待执行列表,当notify_one或者notify_all解锁阻塞线程之后,等待列表中的线程继续执行完毕,并重新加锁,最后退出wait()unique_lock
,因为只有unique_lock
才能够支持同一个线程的重复加解锁。std::condition_variable::wait_for
它的等待方式和wait的区别是增加了一个等待的时间,即除了等待notify_one解锁之外还会有一个时间阈值,当到了这个时间阈值时仍然会退出。wait_for( std::unique_lock& lock, const std::chrono::duration& rel_time)
根据以上条件变量的成员函数,修改代码如下:
#include
#include
#include
#include
#include
#include
using namespace std;std::mutex mu;
std::deque<int> q;
std::condition_variable cond;
/*
线程1使用了 notify_one函数,来用作通知等待线程处理事务。
*/
void function_1(){ int count = 100;while (count > 0) { std::unique_lock<std::mutex> locker(mu);q.push_back(count);locker.unlock();cond.notify_one();//如果环境中有多个等待线程,可以使用`notify_all()`同时触发std::this_thread::sleep_for(std::chrono::seconds(1));count--;}
}
/*
线程2是等待线程,且为了防止伪激活,这里等待线程同时加入了 lamda表达式来标示当
全局队列q也为空的时候线程2仍然处于等待状态。否则伪激活又会像上一套代码中的else
处理解锁消耗系统资源。
*/
void function_2() { int data = 0;while (data != 1) { std::unique_lock<std::mutex> locker(mu);cond.wait(locker,[](){ return !q.empty();});data=q.back();q.pop_back();locker.unlock();std::cout << "t2 got a value from t1 " << data << std::endl; }
}
int main()
{ std::thread t1(function_1);std::thread t2(function_2);t1.join();t2.join();return 0;
}
输出如下
t2 got a value from t1 100
t2 got a value from t1 99
t2 got a value from t1 98
t2 got a value from t1 97
t2 got a value from t1 96
t2 got a value from t1 95
...
...
参考文章:
https://en.cppreference.com/w/cpp/thread/condition_variable
https://zh.cppreference.com/w/cpp/thread/condition_variable/
经过长期探索,发现一个不需要手动设置线程休眠时间(e.g. std::this_thread::sleep_for(std::chrono::microseconds(1)))的代码: Github: https://github.com/log4cplus/ThreadPool #ifndef THREAD_POOL_H_7e...
nth_element(first,nth,last) first,last 第一个和最后一个迭代器,也可以直接用数组的位置。 nth,要定位的第nn 个元素,能对它进行随机访问. 将第n_thn_th 元素放到它该放的位置上,左边元素都小于它,右边元素都大于它. 测试代码: http://www.cplusplus.com...
c/c++老版本的rand()存在一定的问题,在转换rand随机数的范围,类型或者分布时,常常会引入非随机性。
定义在
jsoncpp 是一个C++ 语言实现的json库,非常方便得支持C++得各种数据类型到json 以及 json到各种数据类型的转化。 一个json 类型的数据如下: {"code" : 10.01,"files" : "","msg" : "","uploadid" : "UP000000" } 这种数据类型方便我们人阅读以...
问题如下: 已知一组数(其中有重复元素),求这组数可以组成的所有子集中,子 集中的各个元素和为整数target的子集,结果中无重复的子集。 例如: nums[] = [10, 1, 2, 7, 6, 1, 5], target = 8 结果为: [[1, 7], [1, 2, 5], [2, 6], [1, 1, 6]] 同样之前有...
多线程有什么好处?提高CPU的利用率,更好地利用系统资源,使用Monitor类可以同步静态/实例化的方法的全部代码或者部分代码段,使用不同的同步类创建自己的同步机制。多线程指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程提升整体处理性能。多线程是指程序中包含多个执行流,即...
Step1:在界面主函数的构造函数中初始化多线程 auto mythread = new QThread(); //新建connect(mythread , &QThread::finished, mythread, &QObject::deleteLater);//线程运行结束后释放内存object1->moveToThread...
一、thread的基本用法
参见C++使用thread类多线程编程 。
二、类外使用多线程,访问类的成员
这几种方式,新建线程都是在类外,然后通过把友元函数或者成员函数作为thread参数。
#include
本博文是根据中科大信息学院谭立湘老师的课件加上自己的理解整理出来的 ************************************************************************************ NVIDIA在2007年推出CUDA这个统一计算架构 CUDA的基本思想是支持大量的线程级并...
一、parallel communication patterns 并行通信模式 Map:映射,在特定的位置读取和写入。 Gather:收集,从多个不同的位置读入,写入一个位置。 Scatter:分发,写入多个位置。 Transpose转置 结构数组缩写为AOS,数组结构缩写为SOA 转置运算是指任务重新排序内存中的数...