下面是一个生产者消费者问题,来介绍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 生产者/消费者的更多相关文章

  1. C++11 生产者消费者

    下面是一个生产者消费者问题,来介绍condition_variable的用法.当线程间的共享数据发生变化的时候,可以通过condition_variable来通知其他的线程.消费者wait 直到生产者 ...

  2. C++11实现生产者消费者问题

    生产者消费者问题是多线程并发中一个非常经典的问题.我在这里实现了一个基于C++11的,单生产者单消费者的版本,供大家参考. #include <windows.h> #include &l ...

  3. C++11 并发指南九(综合运用: C++11 多线程下生产者消费者模型详解)

    前面八章介绍了 C++11 并发编程的基础(抱歉哈,第五章-第八章还在草稿中),本文将综合运用 C++11 中的新的基础设施(主要是多线程.锁.条件变量)来阐述一个经典问题——生产者消费者模型,并给出 ...

  4. 综合运用: C++11 多线程下生产者消费者模型详解(转)

    生产者消费者问题是多线程并发中一个非常经典的问题,相信学过操作系统课程的同学都清楚这个问题的根源.本文将就四种情况分析并介绍生产者和消费者问题,它们分别是:单生产者-单消费者模型,单生产者-多消费者模 ...

  5. 再谈多线程模型之生产者消费者(总结)(c++11实现)

    0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...

  6. 再谈多线程模型之生产者消费者(多生产者和多消费者 )(c++11实现)

    0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...

  7. 再谈多线程模型之生产者消费者(多生产者和单一消费者 )(c++11实现)

    0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...

  8. 再谈多线程模型之生产者消费者(单一生产者和多消费者 )(c++11实现)

    0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...

  9. 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现)

    0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现)[本文] 再谈多线程模型之生 ...

随机推荐

  1. LSI SAS 3008配置操作

    配置 LSI SAS 3008 介绍LSISAS3008的配置操作. 4.1 登录CU界面 介绍登录LSISAS3008的CU配置界面的方法. 4.2 创建RAID 介绍在LSISAS3008扣卡上创 ...

  2. Python-elementTree方法解析xml文件-01

    [XML基本概念介绍] XML 指可扩展标记语言(eXtensible Markup Language). XML 被设计用来传输和存储数据.概念一: <foo> # foo元素的起始标签 ...

  3. passwd-shadow文件

    [root@rusky /]# vi /etc/passwd root:x:::Redhat5:/root:/bin/bash rusky:x::::/home/rusky:/bin/bash 1.r ...

  4. 分享一次在Windows Server2012 R2中安装SQL Server2008

    入手一台Windows Server2012云服务器,搭建一下服务环境,选用SQL Server2008 直奔主题,下好安装镜像后,直接双击 选择运行程序而不获取帮助 如图: 进入安装中心后选择 安装 ...

  5. Linux 数据 CD 刻录

    http://www.cyberciti.biz/tips/linux-burning-multi-session-cds-on-linux.html #mkisofs -dvd-video -inp ...

  6. (转) CS0234: 命名空间“System.Web.Mvc”中不存在类型或命名空间名称“Ajax”(是否缺少程序集引用?)

    从SVN上down下来的程序,编译报了一大堆的错,发现是缺少引用,但是明明引用了,后来打开引用,发现system.web.mvc这个引用打着叹号,如图, 后来重新引用了本机的system.web.mv ...

  7. The type or namespace name 'Script' does not exist in the namespace 'System.Web' (are you missing an assembly reference?)

    应该说是 .net4 的bug,没有所谓的 System.Web.Extensions.dll 库文件,需要将项目的 Target Framework修改为 3.5版本,才能加载System.Web. ...

  8. StringEscapeUtils的使用

    使用commons-lang.jar import org.apache.commons.lang.StringEscapeUtils; public class T { public static ...

  9. ORACLE管理存储结构之物理机构+逻辑结构【weber出品】

    一.数据库的存储结构有物理结构和逻辑结构组成的 物理结构:物理上,oracle是由一些操作系统文件组成的 SQL> select name from v$datafile; NAME ----- ...

  10. WdatePicker 设置今天起 后30天可选

    <link href="{:ADDON_PUBLIC_PATH}/style/My97DatePicker/skin/WdatePicker.css" rel="s ...