小编给大家分享一下c++多线程要使用条件变量的原因,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!先看示例1:#include <iOStream&
小编给大家分享一下c++多线程要使用条件变量的原因,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
先看示例1:
#include <iOStream>#include <windows.h>#include <mutex>#include<deque>#include <thread>using namespace std;int nmax = 20;std::deque<int> m_que;std::mutex mymutex;//生产者void producterex(){int i = 1;while (i<nmax){//休眠一秒钟std::this_thread::sleep_for(std::chrono::seconds(1));std::unique_lock<mutex> lcx(mymutex);m_que.push_back(i);cout << "producted:" << i << endl;lcx.unlock();i++;}cout << "product thread exit\n";}//消费者void consumerex(){int i = 0;while (1){std::unique_lock<mutex> lcx(mymutex);if (!m_que.empty()){int i = m_que.back();m_que.pop_back();cout << "consumed:" << i << endl;lcx.unlock();i++;if (i == nmax){break;}}else{lcx.unlock();}}cout << "consumerex thread exit\n";}void main(){std::thread t1(producterex);std::thread t2(consumerex);t1.detach();cout << "hello";t2.detach();cout << " world!\n";getchar();system("pause");}
结果:
可见cpu使用率非常高。高的原因主要在消费者线程中,因为当队列为空的时候它也要执行,做了过多的无用功导致CPU占有率过高,所以下面对进行一个改造让其在空的时候等待200毫秒,相当于增大了轮询间隔周期,应该能降低CPU的占用率。
在这里就贴上消费者的线程,因为其它的都一样。
//消费者void consumerex(){int i = 0;while (1){std::unique_lock<mutex> lcx(mymutex);if (!m_que.empty()){int i = m_que.back();m_que.pop_back();cout << "consumed:" << i << endl;lcx.unlock();i++;if (i == nmax){break;}}else{lcx.unlock();std::this_thread::sleep_for(std::chrono::milliseconds(200));}}cout << "consumerex thread exit\n";}
结果:
可见CPU占用率一下子下降了。
这里就有一个困难了,那就是如何确定休眠的时间间隔(即轮询间隔周期),如果间隔太短会过多占用CPU资源,如果间隔太长会因无法及时响应造成延误。
这就引入了条件变量来解决该问题:条件变量使用“通知—唤醒”模型,生产者生产出一个数据后通知消费者使用,消费者在未接到通知前处于休眠状态节约CPU资源;当消费者收到通知后,赶紧从休眠状态被唤醒来处理数据,使用了事件驱动模型,在保证不误事儿的情况下尽可能减少无用功降低对资源的消耗。
condition_variable介绍
在C++11中,我们可以使用条件变量(condition_variable)实现多个线程间的同步操作;当条件不满足时,相关线程被一直阻塞,直到某种条件出现,这些线程才会被唤醒。
成员函数如下:
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:
a.一个线程因等待"条件变量的条件成立"而挂起;
b.另外一个线程使"条件成立",给出信号,从而唤醒被等待的线程。
为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起;通常情况下这个锁是std::mutex,并且管理这个锁 只能是 std::unique_lockstd::mutex RAII模板类。
上面提到的两个步骤,分别是使用以下两个方法实现:
等待条件成立使用的是condition_variable类成员wait 、wait_for 或 wait_until。
给出信号使用的是condition_variable类成员notify_one或者notify_all函数。
以上两个类型的wait函数都在会阻塞时,自动释放锁权限,即调用unique_lock的成员函数unlock(),以便其他线程能有机会获得锁。这就是条件变量只能和unique_lock一起使用的原因,否则当前线程一直占有锁,线程被阻塞。
虚假唤醒
在正常情况下,wait类型函数返回时要不是因为被唤醒,要不是因为超时才返回,但是在==实际中发现,因此操作系统的原因,wait类型在不满足条件时,它也会返回,这就导致了虚假唤醒。==因此,我们一般都是使用带有谓词参数的wait函数,因为这种(xxx, Predicate pred )类型的函数等价于:
while (!pred()) //while循环,解决了虚假唤醒的问题{ wait(lock);}
原因说明如下:
假设系统不存在虚假唤醒的时,代码形式如下:
if (不满足xxx条件){ //没有虚假唤醒,wait函数可以一直等待,直到被唤醒或者超时,没有问题。 //但实际中却存在虚假唤醒,导致假设不成立,wait不会继续等待,跳出if语句, //提前执行其他代码,流程异常 wait(); }//其他代码...
正确的使用方式,使用while语句解决:
while (!(xxx条件) ){ //虚假唤醒发生,由于while循环,再次检查条件是否满足, //否则继续等待,解决虚假唤醒 wait(); }//其他代码....
下面看一个使用条件变量的情况:
#include <iostream>#include <windows.h>#include <mutex>#include<deque>#include <thread>#include<condition_variable>using namespace std;int nmax = 10;std::deque<int> m_que;std::mutex mymutex;condition_variable mycv;//生产者void producterex(){int i = 1;while (i<nmax){//休眠一秒钟std::this_thread::sleep_for(std::chrono::seconds(1));std::unique_lock<mutex> lcx(mymutex);m_que.push_back(i);cout << "producted:" << i << endl;lcx.unlock();mycv.notify_one();i++;}cout << "product thread exit\n";}//消费者bool m_bflag = false;void consumerex(){int i = 0;bool m_bexit = false;while (!m_bexit){std::unique_lock<mutex> lcx(mymutex);while (m_que.empty()){//避免虚假唤醒mycv.wait(lcx);if (m_bflag){cout << "consumerex thread exit\n";m_bexit = true;break;}}if (m_bexit){break;}int i = m_que.back();m_que.pop_back();lcx.unlock();cout << "consumed:" << i << endl;}cout << "consumerex thread exit\n";}void main(){std::thread t1(producterex);std::thread t2(consumerex);t1.detach();cout << "hello";t2.detach();cout << " world!\n";mycv.notify_one();Sleep(15000);m_que.push_back(100);mycv.notify_one();Sleep(3000);m_bflag = true;mycv.notify_one();//通知线程退出getchar();system("pause");}
结果:
还可以将mycv.wait(lcx);换一种写法,wait()的第二个参数可以传入一个函数表示检查条件,这里使用lambda函数最为简单,如果这个函数返回的是true,wait()函数不会阻塞会直接返回,如果这个函数返回的是false,wait()函数就会阻塞着等待唤醒,如果被伪唤醒,会继续判断函数返回值。代码示例如下:
#include <iostream>#include <windows.h>#include <mutex>#include<deque>#include <thread>#include<condition_variable>using namespace std;int nmax = 10;std::deque<int> m_que;std::mutex mymutex;condition_variable mycv;//生产者void producterex(){int i = 1;while (i<nmax){//休眠一秒钟std::this_thread::sleep_for(std::chrono::seconds(1));std::unique_lock<mutex> lcx(mymutex);m_que.push_back(i);cout << "producted:" << i << endl;lcx.unlock();mycv.notify_one();i++;}cout << "product thread exit\n";}//消费者bool m_bflag = false;void consumerex(){int i = 0;while (1){std::unique_lock<mutex> lcx(mymutex);mycv.wait(lcx, [](){//返回false就继续等待return !m_que.empty();});if (m_bflag){break;}int i = m_que.back();m_que.pop_back();lcx.unlock();cout << "consumed:" << i << endl;}cout << "consumerex thread exit\n";}void main(){std::thread t1(producterex);std::thread t2(consumerex);t1.detach();cout << "hello";t2.detach();cout << " world!\n";mycv.notify_one();Sleep(15000);m_que.push_back(100);mycv.notify_one();Sleep(3000);m_bflag = true;m_que.push_back(-1);mycv.notify_one();//通知线程退出getchar();system("pause");}
以上是“c++多线程要使用条件变量的原因”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网其他教程频道!
--结束END--
本文标题: c++多线程要使用条件变量的原因
本文链接: https://lsjlt.com/news/279215.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-03-01
2024-03-01
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0