c++11 生产者/消费者
下面是一个生产者消费者问题,来介绍condition_variable的用法。当线程间的共享数据发生变化的时候,可以通过condition_variable来通知其他的线程。消费者wait 直到生产者通知其状态发生改变,Condition_variable是使用方法如下:
·当持有锁之后,线程调用wait
·wait解开持有的互斥锁(mutex),阻塞本线程,并将自己加入到唤醒队列中
·当收到通知(notification),该线程从阻塞中恢复,并加入互斥锁队列(mutex queue)
线程被唤醒之后继续持有锁运行。
Condition variable有两种类型:condition_variable 和 condition_variable_any,前一种效率更高,但是使用不够灵活,只支持std::unique_lock<std::mutex>类型的互斥锁;后一种比较灵活,支持所有类型的锁,但是效率稍微低一些。
有一点需要注意的是使用condition variable进行通信的线程,condition variable 需要使用相同的互斥信号量(mutex)。
下面来看例子:(当按下回车键之后停止)


#include <thread> #include <iostream> #include <mutex> #include <queue> #include <condition_variable> #include <atomic> using namespace std; int main()
{ mutex lockBuffer; //申明互斥信号量 volatile bool ArretDemande = false; //使生产、消费过程的结束 queue<long> buffer; condition_variable_any cndNotifierConsommateurs;//condition variable condition_variable_any cndNotifierProducteur; thread ThreadProducteur([&]()//生产者线程
{ std::atomic<long> interlock;//对interlock的操作将是原子的 interlock=1; while(true)
{ std::this_thread::sleep_for (chrono::milliseconds (15)); long element=interlock.fetch_add (1);//【1】 lockBuffer.lock (); while(buffer.size()==10 && ArretDemande ==false)
{ cndNotifierProducteur.wait (lockBuffer);//【2】 } if (ArretDemande==true) { lockBuffer.unlock (); cndNotifierConsommateurs.notify_one ();//【3】 break; } buffer.push(element); cout << "Production unlement :" << element << " size :" << buffer.size() << endl; lockBuffer.unlock (); cndNotifierConsommateurs.notify_one (); } } );
thread ThreadConsommateur([&]()
{ while(true)
{ lockBuffer.lock (); while(buffer.empty () && ArretDemande==false) { cndNotifierConsommateurs.wait(lockBuffer); } if (ArretDemande==true && buffer.empty ()) { lockBuffer.unlock(); cndNotifierProducteur.notify_one (); break; } long element=buffer.front(); buffer.pop (); cout << "Consommation element :" << element << " size :" << buffer.size() << endl; lockBuffer.unlock (); cndNotifierProducteur.notify_one (); } } ); std::cout << "Pour arreter pressez [ENTREZ]" << std::endl; getchar(); std::cout << "Arret demande" << endl
ArretDemande=true; ThreadProducteur.join();
ThreadConsommateur.join(); cout<<"Main Thread"<<endl; return 0; }


运行结果:

对程序进行一下说明,程序中有三个线程,主线程、生产者线程、消费者线程,三个线程之间乱序执行,通过一些全局变量来控制他们的执行顺序。主线程的作用是控制生产消费过程是否结束,当程序运行之后,主线程通过getchar()接收一个输入,接收到输入后会将ArretDemande设置为true,另外两个线程会终止。生产者线程将生产出来的数据放在一个queue类型的buffer中,并解锁,通知消费之线程,buffer中最多“能”存10个数据,如果buffer中已经有10个数据还没有被取走,则会通知消费者线程“消费”,如果ArretDmande被置位,则打开锁,并通知消费之线程。消费者线程主要是将buffer中的数据取出来,当buffer为空的时候阻塞自己,并通知生产者线程,当ArretDemande被置位,且已经消费完产品则解锁,并通知生产者线程。需要注意的是需要通信的生产者和消费者这两个线程通过condition variable来实现通信,必须操作同一个mutex,这里是lockbuffer,并且每次Notify都会打开当前锁。
程序中对interlock进行的操作是原子的,interlock.fet_add(N),效果是将interlock加N,然后返回interlock在加N之前的值,atomic类型是通过一定的内存顺序规则来实现这个过程的。
虽然conditon_variable 只能支持std::unique_lock<std::mutex>类型的互斥锁,但是在大部分情况下已经够用,而且使用std::unique_lock<std::mutex>会比较简单,因为std::unique_lock<std::mutex>在声明的时候就会初始化,在生命周期结束之后就会自动解锁,因此我们不用太花精力来考虑什么时候解锁。我们来看看下面这段程序:


#include <condition_variable>
#include <mutex>
#include <thread>
#include <iostream>
#include <queue>
#include <chrono> int main()
{
std::queue<int> produced_nums;
std::mutex m;;
std::condition_variable cond_var;
bool done = false;
bool notified = false; std::thread producer([&]() {
for ( int i = 0; i < 5; ++i) {
std::this_thread::sleep_for(std::chrono:: seconds(1));
std:: unique_lock<std::mutex > lock(m); //May lock mutex after construction, unlock before destruction.
std::cout << "producing " << i << '\n' ;
produced_nums.push(i);
notified = true;
cond_var.notify_one();
} done = true;
cond_var.notify_one();
});
//cond_var.notify_one();
std::thread consumer([&]() {
while (!done) {
std:: unique_lock<std::mutex > lock(m);
while (!notified) { // loop to avoid spurious wakeups
cond_var.wait(lock);
}
while (!produced_nums.empty()) {
std::cout << "consuming " << produced_nums.front() << '\n';
produced_nums.pop();
}
notified = false;
}
}); producer.join();
consumer.join(); return 0;
}


运行结果:
C:\Windows\system32\cmd.exe /c producer_consumer.exe
producing 0
consuming 0
producing 1
consuming 1
producing 2
consuming 2
producing 3
consuming 3
producing 4
consuming 4
Hit any key to close this window...
c++11 生产者/消费者的更多相关文章
- C++11 生产者消费者
下面是一个生产者消费者问题,来介绍condition_variable的用法.当线程间的共享数据发生变化的时候,可以通过condition_variable来通知其他的线程.消费者wait 直到生产者 ...
- C++11实现生产者消费者问题
生产者消费者问题是多线程并发中一个非常经典的问题.我在这里实现了一个基于C++11的,单生产者单消费者的版本,供大家参考. #include <windows.h> #include &l ...
- C++11 并发指南九(综合运用: C++11 多线程下生产者消费者模型详解)
前面八章介绍了 C++11 并发编程的基础(抱歉哈,第五章-第八章还在草稿中),本文将综合运用 C++11 中的新的基础设施(主要是多线程.锁.条件变量)来阐述一个经典问题——生产者消费者模型,并给出 ...
- 综合运用: C++11 多线程下生产者消费者模型详解(转)
生产者消费者问题是多线程并发中一个非常经典的问题,相信学过操作系统课程的同学都清楚这个问题的根源.本文将就四种情况分析并介绍生产者和消费者问题,它们分别是:单生产者-单消费者模型,单生产者-多消费者模 ...
- 再谈多线程模型之生产者消费者(总结)(c++11实现)
0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...
- 再谈多线程模型之生产者消费者(多生产者和多消费者 )(c++11实现)
0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...
- 再谈多线程模型之生产者消费者(多生产者和单一消费者 )(c++11实现)
0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...
- 再谈多线程模型之生产者消费者(单一生产者和多消费者 )(c++11实现)
0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...
- 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现)
0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现)[本文] 再谈多线程模型之生 ...
随机推荐
- Swift中的协议
协议: 1.Swift协议用于定义多个类型应该遵守的规范 2.协议定义了一种规范, 不提供任何实现 3.协议统一了属性名, 方法, 下标, 但是协议并不提供任何实现 4.语法格式: [修饰符] pro ...
- Flask学习记录之Flask-Moment
Moment.js 是一个简单易用的轻量级JavaScript日期处理类库,提供了日期格式化.日期解析等功能.它支持在浏览器和NodeJS两种环境中运行.此类库能够 将给定的任意日期转换成多种不同的格 ...
- C++得到最大的int值
要得到最大的int值: 1.利用(unsigned int)-1,这样得到的就是unsigned int表示的最大值. 2.int值只是比unsigned int多一位符号位,所以对(unsigned ...
- C语言初学 计算二元一次方程的问题
#include<stdio.h> #include<math.h> int main() { double a,b,c,disc,x1,x2; scanf("%lf ...
- iOS界面调试工具 Reveal-备用
Reveal是一个iOS程序界面调试工具.使用Reveal,我们可以在iOS开发时动态地查看和修改应用程序的界面. 对于动态或复杂的交互界面,手写UI是不可避免的.通过Reveal,我们可以方便地调试 ...
- shell下 使用心得
打印时间的命令 date +'%F %k:%M:%d' crontab启动计划任务,注意两件事情: 1)crontab里启动的脚本,如果需要读取文件,需要使用绝对路径,或者在脚本里cd到目录所在的绝对 ...
- vim 折叠代码技巧汇总
以下命令输入的方式: 如zo命令,先按z键,松开后按o键即可展开折叠. 一.打开.关闭折叠 zo 展开折叠,只展开最外层的折叠. zO 对所在范围内所有嵌套的折叠点展开,包括嵌套折叠. zc 折叠,只 ...
- performance
简介 延缓执行 JavaScript 是一个能有效提高网页加载速度以及提升用户阅读体验质量的途径.从实际经验来看,将我们的网站从经济实惠的 VPS 迁移到 Softlayer(美国著名的一个数据中心) ...
- USART笔记 基于STM32F107VCT6
USART 通用同步异步收发器 通用同步异步收发器(USART)提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换.USART利用分数波特率发生器提供宽范围的 ...
- 开始使用Logstash
开始使用Logstash 本节将指导处理安装Logstash 和确认一切是运行正常的, 后来的章节处理增加负载的配置来处理选择的使用案例. 这个章节包含下面的主题: Installing Logsta ...