关于Condition Variable的一些思考
可能大家都使用过condition variable(之后称cv),一些博客也对cv做了介绍,但是有的说的不完全正确,甚至有误导使用者的倾向,其实最合理的使用方式是查阅文档,
如果你英语还ok的话,http://en.cppreference.com/w/cpp/thread/condition_variable 读一遍即可 完全没有必要看我写的这些砖块,但是我的blog可能思路上更贴近初学者,可以借鉴一下。
背景不过多做介绍,使用condition variable的时候都要配合mutex使用,那么mutex就是为了什么呢,为什么要用这个东西呢?
为什么要这样设计?mutex多余吗?他是用来保护什么数据呢?
ok!让我们来从零开始。
condition variable的初衷是为了wait某一个东东变成true或false,然后此线程继续进行。
按照此需求,大概的代码应该是这样:
//version 1.0
Thread 1:
cv.wait();
Thread 2:
cv.signal();
这样的话有一个很严重的问题,如果t2先执行,那么t1岂不是永远停在那里了!这样与我们最初的构想完全不符!
于是产生了下面的写法:
//version 2.0
bool signaled = false;
thread 1:
if(!signaled)
cv.wait();
thread 2:
cv.signal();
signaled = true;
看上去不错吧!但是犯了一个很白痴的错误,bool并不是thread safe的,所以
//version 2.1
bool signaled = false;
thread 1:
lock;
if(!signaled) //1
unlock;
cv.wait(); //2
thread 2:
cv.signal(); //3
lock;
signaled = true; //4
unlock;
这样我们把signaled保护起来,保证thread safe了,会有什么问题吗?
当然有问题!如果执行顺序是这样的话,3-1-4-2 那么thread 1永远wait在那里了!
欸!为什么会这样呢!让我们从长计议!一开始为了保证signal是否发出,我们增加了一个标志位来判断是否信号已经发出,如果已经发出,那么cv不需要等待。接着,我们对thread safe进行了保证,然后现在又出现了执行顺序的问题!
仔 细思考一下,就会发现,其实导致问题的原因就在于需求的原子性并没有很好的用c++表达出来,你想想,thread 1的意思翻译成汉语就是“如果没有信号没有触发,那么我就等”,thread 2的意思很明显就是“触发!”,但是触发一句话到了c++语义中就变成了两个语句,多个指令,那么你很难满足我的需求,至少在不进行原子性语句约束的情况 下你无法满足我的需求。
所以 thread 1 这段代码就是一团糟!当进行wait操作之前,你能确定signaled为false吗?答案是否定的。
或者说thread 2 这段代码 当你signal之后,你的signaled马上变成true了吗? 答案也是否定的,你如果无法保证这两个语句的原子性,那么就无法达到需求。
所以我们继续修改:
//version 3.0
bool signaled = false;
thread 1:
lock;
if(!signaled) //1
cv.wait(); //2
unlock;
thread 2:
lock;
cv.signal(); //3
signaled = true; //4
unlock;
这样就ok了!但是如果你听说过spurious wakeup(可以自行查阅一下),那么3.0依然是不够完善的,
最终的版本是:
//version 4.0
bool signaled = false;
thread 1:
lock;
while(!signaled) //1
cv.wait(); //2
unlock;
thread 2:
lock;
cv.signal(); //3
signaled = true; //4
unlock;
然后我们回到最初的问题上去,为什么需要传入一个参数mutex,答案就很显然了,是为了让cv进行unlock操作的,当cv拿回线程的权杖的时候,再把mutex lock上,保证对称性。
关于Condition Variable的一些思考的更多相关文章
- C++关于Condition Variable
#include <condition_variable> #include <mutex> #include <future> #include <iost ...
- 关于condition variable的理解
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255) ...
- c++并发编程之条件变量(Condition Variable)
条件变量(Condition Variable)的一般用法是:线程 A 等待某个条件并挂起,直到线程 B 设置了这个条件,并通知条件变量,然后线程 A 被唤醒.经典的「生产者-消费者」问题就可以用条件 ...
- C++11中的mutex, lock,condition variable实现分析
本文分析的是llvm libc++的实现:http://libcxx.llvm.org/ C++11中的各种mutex, lock对象,实际上都是对posix的mutex,condition的封装.不 ...
- Condition Variable使用及其Thread Cancellation线程取消
条件变量Condition Variable的一般用法: 唤醒用法: struct { pthread_mutex_t mutex; pthread_cond_t cond; //whatever v ...
- 第8章 用户模式下的线程同步(4)_条件变量(Condition Variable)
8.6 条件变量(Condition Variables)——可利用临界区或SRWLock锁来实现 8.6.1 条件变量的使用 (1)条件变量机制就是为了简化 “生产者-消费者”问题而设计的一种线程同 ...
- [转] 条件变量(Condition Variable)详解
http://www.wuzesheng.com/?p=1668 条件变量(Condtion Variable)是在多线程程序中用来实现“等待->唤醒”逻辑常用的方法.举个简单的例子,应用程序A ...
- 条件变量(Condition Variable)详解
条件变量(Condtion Variable)是在多线程程序中用来实现“等待->唤醒”逻辑常用的方法.举个简单的例子,应用程序A中包含两个线程t1和t2.t1需要在bool变量test_cond ...
- 使用Condition Variables 实现一个线程安全队列
使用Condition Variables实现一个线程安全队列 测试机: i7-4800MQ .7GHz, logical core, physical core, 8G memory, 256GB ...
随机推荐
- Android的Bitmap和BitmapDrawable类解析-android学习之旅(六十)
使用简单图片 使用Drawable对象 bitmap和BitmapDrawable对象 package peng.liu.test; import android.app.Activity; impo ...
- random background
function roll(){ var bg = document.getElementById("loginbg"); var rnd = Math.floor(Math.ra ...
- 在一个apk中调用另外一个apk中的activity
今天忽然想到如果要在一个activity中调用另外一个activity该怎么办呢? 感觉这个应该比较简单,应为activity的启动方式就两种:显式启动.隐式启动: 显式启动的话肯定不行,那就只能使用 ...
- android通讯录导航栏源码(一)
通讯录导航栏源码: 1.activity package com.anna.contact.activity; import java.util.ArrayList; import java.util ...
- tomcat 发布webService
<!-- tomcat发布webservice时所需jar --> <dependency> <groupId>com.sun.xml.ws</groupId ...
- Linux下安装McAfee防病毒软件(企业版本)
最近公司接一个项目虚拟化解决方案,不过所有硬件设备不是我们采购的,我们只是负责软体安装.我看了一下那个硬件设备那叫高,不过目前还到那边去安装,那边硬件还没安装完成,然后Boss给我拿来两台新服务器,让 ...
- ExecutorService 的理解与使用
ExecutorService 的理解与使用 http://my.oschina.net/bairrfhoinn/blog/177639 Java线程池ExecutorService http:// ...
- Kurskal算法(克鲁斯卡尔算法)
特点:适用于稀疏图,边比较少的图.如果顶点较少,且为稠密图,则用Prim算法.跟Prim算法的用途相同.时间复杂度为O(e*loge),其中e为边数. 代码: #include <stdio.h ...
- BZOJ 1564: [NOI2009]二叉查找树( dp )
树的中序遍历是唯一的. 按照数据值处理出中序遍历后, dp(l, r, v)表示[l, r]组成的树, 树的所有节点的权值≥v的最小代价(离散化权值). 枚举m为根(p表示访问频率): 修改m的权值 ...
- Qt中实现菜单和工具栏功能
Qt创建菜单和工具栏: 一. temp.h文件的内容 1.定义类temp继承QMainWindow 2.添加Q_OBJECT , 构造函数 . 析构函数等内容 3.$重点内容 3.1定义QActio ...