C++并发编程 等待与唤醒

条件变量

条件变量, 包括(std::condition_variable 和 std::condition_variable_any)
  定义在 condition_variable 头文件中, 它们都需要与互斥量(作为同步工具)一起才能工作.

  std::condition_variable 允许阻塞一个线程, 直到条件达成.

成员函数

void wait(std::unique_lock<std::mutex>& lock);
等待, 通过 notify_one(), notify_all()或伪唤醒结束等待
void wait(std::unique_lock<std::mutex>& lock, Predicate pred);
等待, 通过 notify_one(), notify_all()被调用, 并且谓词为 true 时结束等待.
pred 谓词必须是合法的, 并且需要返回一个值, 这个值可以和bool互相转化
cv_status wait_until(std::unique_lock<std::mutex>& lock, const std::chrono::time_point<Clock, Duration>& absolute_time);
调用 notify_one(), notify_all(), 超时或线程伪唤醒时, 结束等待.
返回值标识了是否超时.
bool wait_until(std::unique_lock<std::mutex>& lock, const std::chrono::time_point<Clock, Duration>& absolute_time, Predicate pred);
等待, 通过 notify_one(), notify_all(), 超时, 线程伪唤醒, 并且谓词为 true 时结束等待.
cv_status wait_for(std::unique_lock<std::mutex>& lock, const std::chrono::duration<Rep, Period>& relative_time);
调用 notify_one(), notify_all(), 指定时间内达成条件或线程伪唤醒时,结束等待
bool wait_for(std::unique_lock<std::mutex>& lock, const std::chrono::duration<Rep, Period>& relative_time, Predicate pred);
调用 notify_one(), notify_all(), 指定时间内达成条件或线程伪唤醒时,并且谓词为 true 时结束等待.

void notify_one() noexcept; 唤醒一个等待当前 std::condition_variable 实例的线程
void notify_all() noexcept; 唤醒所有等待当前 std::condition_variable 实例的线程

一个线程安全的队列设计:

    #ifndef _THREAD_SAFE_QUEUE_
#define _THREAD_SAFE_QUEUE_ #include <condition_variable>
#include <mutex>
#include <queue>
#include <memory> template<typename Ty, typename ConditionVar = std::condition_variable, typename Mutex = std::mutex>
class ThreadSafeQueue
{
typedef std::queue<Ty> Queue;
typedef std::shared_ptr<Ty> SharedPtr;
typedef std::lock_guard<Mutex> MutexLockGuard;
typedef std::unique_lock<Mutex> MutexUniqueLock; public:
explicit ThreadSafeQueue() {}
~ThreadSafeQueue() {} ThreadSafeQueue(const ThreadSafeQueue&) = delete;
ThreadSafeQueue& operator=(const ThreadSafeQueue&) = delete; bool IsEmpty() const
{
MutexLockGuard lk(mMutex);
return mQueue.empty();
} void WaitAndPop(Ty& value)
{
MutexUniqueLock lk(mMutex);
mConVar.wait(lk, [this] {
return !mQueue.empty();
});
value = mQueue.front();
mQueue.pop();
} SharedPtr WaitAndPop()
{
MutexUniqueLock lk(mMutex);
mConVar.wait(lk, [this] {
return !mQueue.empty();
});
SharedPtr sp = std::make_shared<Ty>(mQueue.front());
mQueue.pop();
return sp;
} bool TryPop(Ty& value)
{
MutexLockGuard lk(mMutex);
if (mQueue.empty())
return false;
value = mQueue.front();
mQueue.pop();
return true;
} SharedPtr TryPop()
{
MutexLockGuard lk(mMutex);
if (mQueue.empty())
return false;
SharedPtr sp = std::make_shared<Ty>(mQueue.front());
mQueue.pop();
return sp;
} void Push(const Ty& value)
{
MutexLockGuard lk(mMutex);
mQueue.push(value);
mConVar.notify_all();
} private:
mutable Mutex mMutex;
ConditionVar mConVar;
Queue mQueue;
}; #endif // _THREAD_SAFE_QUEUE_

另一个版本, 使用 shared_ptr 作为成员对队列的性能有很大的提升, 其在push时减少了互斥量持有的时间, 允许其它线程在分配内存的同时,对队列进行其它操作.

template<typename Ty, typename ConditionVar = std::condition_variable, typename Mutex = std::mutex>
class ThreadSafeQueue
{
typedef std::shared_ptr<Ty> SharedPtr;
typedef std::queue<SharedPtr> Queue;
typedef std::shared_ptr<Ty> SharedPtr;
typedef std::lock_guard<Mutex> MutexLockGuard;
typedef std::unique_lock<Mutex> MutexUniqueLock; public:
explicit ThreadSafeQueue() {}
~ThreadSafeQueue() {} ThreadSafeQueue(const ThreadSafeQueue&) = delete;
ThreadSafeQueue& operator=(const ThreadSafeQueue&) = delete; bool IsEmpty() const
{
MutexLockGuard lk(mMutex);
return mQueue.empty();
} void WaitAndPop(Ty& value)
{
MutexUniqueLock lk(mMutex);
mConVar.wait(lk, [this] {
return !mQueue.empty();
});
value = std::move(*mQueue.front());
mQueue.pop();
} SharedPtr WaitAndPop()
{
MutexUniqueLock lk(mMutex);
mConVar.wait(lk, [this] {
return !mQueue.empty();
});
SharedPtr sp = mQueue.front();
mQueue.pop();
return sp;
} bool TryPop(Ty& value)
{
MutexLockGuard lk(mMutex);
if (mQueue.empty())
return false;
value = std::move(*mQueue.front());
mQueue.pop();
return true;
} SharedPtr TryPop()
{
MutexLockGuard lk(mMutex);
if (mQueue.empty())
return false;
SharedPtr sp = mQueue.front();
mQueue.pop();
return sp;
} void Push(const Ty& value)
{
SharedPtr p = std::make_shared<Ty>(value);
MutexLockGuard lk(mMutex);
mQueue.push(p);
mConVar.notify_all();
} private:
mutable Mutex mMutex;
ConditionVar mConVar;
Queue mQueue;
};

std::future

期望(std::future)可以用来等待其他线程上的异步结果, 其实例可以在任意时间引用异步结果.
C++包括两种期望, std::future(唯一期望) 和 std::shared_future(共享期望)
std::future 的实例只能与一个指定事件相关联.
std::shared_future 的实例能关联多个事件, 它们同时变为就绪状态, 并且可以访问与事件相关的任何数据.
在与数据无关的地方,可以使用 std::future<void> 与 std::shared_future<void> 的特化模板.
期望对象本身并不提供同步访问, 如果多个线程要访问一个独立的期望对象, 需要使用互斥体进行保护.

std::packaged_task

std::packaged_task 可包装一个函数或可调用的对象, 并且允许异步获取该可调用对象产生的结果, 返回值通过 get_future 返回的 std::future 对象取得, 其返回的 std::future 的模板类型为 std::packaged_task 模板函数签名中的返回值类型.
std::packaged_task 对象被调用时, 就会调用相应的函数或可调用对象, 将期望置为就绪, 并存储返回值.
std::packaged_task 的模板参数是一个函数签名, 如 int(std::string&, double*), 构造 std::packaged_task 实例时必须传入一个可以匹配的函数或可调用对象, 也可以是隐藏转换能匹配的.

    std::packaged_task<std::string(const std::string&)> task([](std::string str) {
std::stringstream stm;
stm << "tid:" << std::this_thread::get_id() << ", str:" << str << std::endl;
std::cout << stm.str();
std::this_thread::sleep_for(std::chrono::seconds());
return std::string("MSG:Hello");
});
std::future<std::string> f = task.get_future();
std::thread t(std::move(task), std::string("package task test"));
t.detach();
// 调用 f.get 返回结果, 但是须阻塞等到任务执行完成
std::cout << "main tid:" << std::this_thread::get_id() << ", result: " << f.get() << std::endl;

std::promise

std::promise 类型模板提供设置异步结果的方法, 这样其他线程就可以通过 std::future 实例来索引该结果.

    class SquareRoot
{
std::promise<double>& prom;
public:
SquareRoot(std::promise<double>& p) : prom(p) {}
~SquareRoot() {}
void operator()(double x)
{
if (x < )
{
prom.set_exception(std::make_exception_ptr(std::out_of_range("x<0")));
}
else
{
double result = std::sqrt(x);
prom.set_value(result);
}
}
}; std::promise<double> prom;
SquareRoot p(prom);
std::thread t(std::bind(&SquareRoot::operator(), &p, ));
//std::thread t(std::bind(&SquareRoot::operator(), &p, -1));
std::future<double> f = prom.get_future();
try
{
double v = f.get();
std::cout << "value:" << v << std::endl;
}
catch (std::exception& e)
{
std::cout << "exception:" << e.what() << std::endl;
}
t.join();

C++并发编程 等待与唤醒的更多相关文章

  1. 【Java并发编程实战】----- AQS(三):阻塞、唤醒:LockSupport

    在上篇博客([Java并发编程实战]----- AQS(二):获取锁.释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起:在释放锁后,需要唤醒该线程的继任节点 ...

  2. 超强图文|并发编程【等待/通知机制】就是这个feel~

    你有一个思想,我有一个思想,我们交换后,一个人就有两个思想 If you can NOT explain it simply, you do NOT understand it well enough ...

  3. Java并发编程实战 05等待-通知机制和活跃性问题

    Java并发编程系列 Java并发编程实战 01并发编程的Bug源头 Java并发编程实战 02Java如何解决可见性和有序性问题 Java并发编程实战 03互斥锁 解决原子性问题 Java并发编程实 ...

  4. [笔记][Java7并发编程实战手冊]3.4 等待多个并发事件的完毕CountDownLatch倒计数闭锁

    [笔记][Java7并发编程实战手冊]系列文件夹 简单介绍 本文学习CountDownLatch 倒计数闭锁. 本人英文不好.靠机器翻译,然后有一段非常形象的描写叙述,让我把它叫为倒计数 用给定的计数 ...

  5. Java并发编程-JUC-CountDownLatch 倒计数门闩器-等待多线程完成再放行 -一次性使用

    如题 (总结要点) CountDownLatch 倒计数门闩器, 让1-n-1个线程等待其他多线程完成工作. (Excel的多个Sheet的解析,最终等待解析完毕后;要实现主线程等待所有线程完成she ...

  6. 【Java并发编程实战】----- AQS(四):CLH同步队列

    在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形.其主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头 ...

  7. 【Java并发编程实战】----- AQS(二):获取锁、释放锁

    上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...

  8. 【Java并发编程实战】-----“J.U.C”:Exchanger

    前面介绍了三个同步辅助类:CyclicBarrier.Barrier.Phaser,这篇博客介绍最后一个:Exchanger.JDK API是这样介绍的:可以在对中对元素进行配对和交换的线程的同步点. ...

  9. 【Java并发编程实战】-----“J.U.C”:CyclicBarrier

    在上篇博客([Java并发编程实战]-----"J.U.C":Semaphore)中,LZ介绍了Semaphore,下面LZ介绍CyclicBarrier.在JDK API中是这么 ...

随机推荐

  1. Deeplearning - Overview of Convolution Neural Network

    Finally pass all the Deeplearning.ai courses in March! I highly recommend it! If you already know th ...

  2. Apriori 获取关联规则实现

    前言 自己的一个Apriori 获取关联规则的python实现.具体原理不讲,代码添加了说明,还是很好理解的. 数据预处理 #最小置信度 min_conf = 0.5 #最小支持度 min_sup = ...

  3. Python爬虫入门(3-4):Urllib库的高级用法

    1.分分钟扒一个网页下来 怎样扒网页呢?其实就是根据URL来获取它的网页信息,虽然我们在浏览器中看到的是一幅幅优美的画面,但是其实是由浏览器解释才呈现出来的,实质它 是一段HTML代码,加 JS.CS ...

  4. BZOJ 4945 NOI2017 游戏 搜索+2-SAT

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4945 分析: 首先考虑没有x的情况,发现有一个明显的推理模型,容易看出来可以用2-SAT ...

  5. 互评Alpha版本——基于NABCD评论作品,及改进建议

    组名:可以低头,但没必要 组长:付佳 组员:张俊余  李文涛  孙赛佳  田良  于洋  刘欣  段晓睿 一.杨老师粉丝群--<弹球学成语> 1.1 NABCD分析   N(Need,需求 ...

  6. 20181120-6 Beta阶段第2周/共2周 Scrum立会报告+燃尽图 03

    此作业要求参见:[https://edu.cnblogs.com/campus/nenu/2018fall/homework/2411] 版本控制地址    [https://git.coding.n ...

  7. 冲刺ing-3

    第三次Scrum冲刺 队员完成的任务 队员 完成任务 吴伟华 分配任务,燃尽图 蔺皓雯 编写博客,美化主界面 蔡晨旸 美化主界面 曾茜 主页面设计 鲁婧楠 服务器建构 杨池宇 服务器建构 成员遇到的问 ...

  8. C++:const_cast的简单理解

    前言:const_cast是我比较头疼的一个知识点,最近查阅了很多资料,也翻看了很多他人的博客,故在此将自己目前学习到的有关const_cast知识做一个简单的总结 一.什么是const_cast 简 ...

  9. 2018软工实践—Alpha冲刺(5)

    队名 火箭少男100 组长博客 林燊大哥 作业博客 Alpha 冲鸭鸭鸭鸭鸭! 成员冲刺阶段情况 林燊(组长) 过去两天完成了哪些任务 协调各成员之间的工作 协助测试的进行 测试项目运行的服务器环境 ...

  10. c++第三次作业

    GitHub地址 https://github.com/ronghuijun/3Elevators-scheduling 实现过程 一开始打算分成三个类来写的 因为想到电梯的功能不太一样 一个只能上1 ...