当前位置: 首页 > news >正文

【C++11并发】future库 笔记

简介

C++11之前,主线程要想获取子线程的返回值,一般都是通过全局变量,或者类似机制。C++11开始为我们提供了一组方法来获取子线程的返回值,并保证其原子性。

头文件

#include <future>

std::promise

在promise中保存了一个值或者异常,可以通过与其关联的 std::future 来获取这个值或者异常。常规用法

promise有两个特化的版本

template< class R > class promise;    // 普通类模板
template< class R > class promise<R&>;    // 引用特化
template<> class promise<void>;    // void特化

promise提供的方法
在这里插入图片描述
构造方法

promise();    // 默认构造方法
template< class Alloc >
promise( std::allocator_arg_t, const Alloc& alloc );    // cppreference上说是基于一个共享状态构造promise,还没有深入研究具体怎么用
promise( promise&& other ) noexcept;    // 移动构造方法
promise( const promise& other ) = delete;    // 删除了拷贝构造方法

析构方法
析构的时机有两个

  • promise内部共享状态为ready时
  • 如果promise的状态没有被设置为ready析构时,会将std::future_error异常保存到共享状态里

和构造方法类似,promise只支持移动赋值操作符

promise& operator=( promise&& other ) noexcept;
promise& operator=( const promise& rhs ) = delete;

获取future,如果get_future是被重复调用的,那么就会抛出std::future_error异常。如果想多个地方获取future,需要使用std::shared_future,他可以通过std::future构造。这是std::shared_future相应的构造方法的声明:shared_future( std::future&& other )

std::future<R> get_future();

设置共享数据,以及将共享状态设置为ready。由于promise有特化版本,下面声明的方法有些版本没有。另外,如果重复调用set_value会抛出std::future_error异常,即set_value只能调用一次。

void set_value( const R& value );    // member only of generic promise template
void set_value( R&& value );     //member only of generic promise template
void set_value( R& value );     // member only of promise<R&> template specialization
void set_value();    // 设置共享状态为ready

设置异常,以及将共享状态设置为ready,例子

void set_exception( std::exception_ptr p );

set_value_at_thread_exit 和 set_exception_at_thread_exit 类似set_value 和 set_exception,不同点是 xx_at_thread_exit 是在线程结束的时候将共享状态设置为ready,而set_xx 是立刻设置为ready。

std::future

future类是获取异步操作返回的封装。promise相当于是set,future相当于是get

类似promise,future也有两个特化版本

template< class T > class future;
template< class T > class future<T&>;
template<> class future<void>;

future提供的方法
在这里插入图片描述
构造方法

future() noexcept;    // 不关联任何共享状态
future( future&& other )     // 移动构造
future( const future& other ) = delete;    // 拷贝构造被删除

只有移动赋值操作符

future& operator=( future&& other ) noexcept;
future& operator=( const future& other ) = delete;

从构造方法和移动赋值操作符可以看出,future是唯一关联共享状态的,如果想多个future关联一个共享状态,future是不行的,需要将其转为std::shared_future。对应的方法是share:

std::shared_future<T> share() noexcept;

share方法内部是这么构造shared_future的:std::shared_future(std::move(*this)),所以调用share方法后,原理的future就不在关联共享状态了,也就不能再调用相关get方法,否则会抛出异常

std::shared_future

和std::future唯一的不同就是,std::shared_future可以多个对象同时关联同一个共享状态。他的特化版本、提供的方法和std::future也基本类似:
在这里插入图片描述

std::packaged_task

将可调用对象(函数,lambda表达式,仿函数等)包装后,可以异步调用,通过std::future获取返回值。参考代码

std::packaged_task 提供的方法
在这里插入图片描述

构造方法

packaged_task() noexcept;template< class F >
explicit packaged_task( F&& f );    // f为可调用对象,他是一个万能引用,即构造package_task的时候,可以传递给他可调用对象的左值,右值,左值引用,右值引用;参考:https://zhuanlan.zhihu.com/p/99524127template< class F, class Allocator >
explicit packaged_task( std::allocator_arg_t, const Allocator& a, F&& f );    // C++17删除了该方法packaged_task( const packaged_task& ) = delete;packaged_task( packaged_task&& rhs ) noexcept;    // 移动构造方法

析构方法
如果在共享状态设置为ready之前析构package_task,会抛出 std::future_errc::broken_promise 异常

只有移动赋值操作符

packaged_task& operator=( const packaged_task& ) = delete;
packaged_task& operator=( packaged_task&& rhs ) noexcept;

判断package_task对象是否有共享状态,比如用无参的构造方法构造的对象,就没有共享状态

bool valid() const noexcept;

获取future,如果重复调用get_future,会抛出std::future_error异常;另外,如果valid()方法返回为false,调用get_future,也会抛出std::future_error异常

std::future<R> get_future();

执行保存的可调用对象,和get_future一样重复调用,或者pakage_task对象没有共享状态,都会抛出std::future_error异常

void operator()( ArgTypes... args );

在线程结束,局部变量释放后,将共享状态设置为ready。抛出异常的情况和operator()一样

void make_ready_at_thread_exit( ArgTypes... args );

重置package_task的共享状态,等价于 *this = packaged_task(std::move(f)), f 是可调用对象。此时会构造新的共享状态,如果没有足够内存,会抛出std::bad_alloc异常。如果package_task对象没有共享状态,就会抛出std::future_error异常

void reset();

std::async

异步直行一个可调用对象
在这里插入图片描述

enum class launch : /* unspecified */ {async =    /* unspecified */,    // 立刻异步直行deferred = /* unspecified */,    // /* implementation-defined */
};

当使用launch::deferred时,只有future调用get或者wait方法时,才会异步直行可调用对象

cppreference

相关文章:

  • Pinia 和 Vuex 的对比
  • 构建智能医患沟通:陪诊小程序开发实战
  • 不存在类型变量 A, T 的实例,使 Collector<T, A, List<T>> 符合 Supplier<R>
  • Django 入门学习总结3
  • 使用 PowerShell 创建共享目录
  • 2023亚太杯数学建模思路 - 案例:粒子群算法
  • 【Redis篇】简述Java中操作Redis的方法
  • 【广州华锐互动】昆虫3D虚拟动态展示:探索神奇的微观世界
  • jjwt使用说明-笔记
  • win10 怎么进入cmd窗口
  • VBA技术资料MF85:将工作簿批量另存为PDF文件
  • django ModelSerializer自定义显示字段
  • msvcp140.dll是什么?msvcp140.dll丢失的有哪些解决方法
  • 90.子集II
  • Linux中的进程程序替换
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • 【腾讯Bugly干货分享】从0到1打造直播 App
  • Angular Elements 及其运作原理
  • CentOS7 安装JDK
  • E-HPC支持多队列管理和自动伸缩
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • GraphQL学习过程应该是这样的
  • Hexo+码云+git快速搭建免费的静态Blog
  • HTTP那些事
  • Java读取Properties文件的六种方法
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • Octave 入门
  • PV统计优化设计
  • spring-boot List转Page
  • 道格拉斯-普克 抽稀算法 附javascript实现
  • 力扣(LeetCode)357
  • 如何使用 JavaScript 解析 URL
  • 我的面试准备过程--容器(更新中)
  • 想使用 MongoDB ,你应该了解这8个方面!
  •  一套莫尔斯电报听写、翻译系统
  • 怎么把视频里的音乐提取出来
  • UI设计初学者应该如何入门?
  • ​力扣解法汇总1802. 有界数组中指定下标处的最大值
  • ###C语言程序设计-----C语言学习(6)#
  • #define用法
  • (js)循环条件满足时终止循环
  • (MonoGame从入门到放弃-1) MonoGame环境搭建
  • (附源码)spring boot火车票售卖系统 毕业设计 211004
  • (顺序)容器的好伴侣 --- 容器适配器
  • (原創) 人會胖會瘦,都是自我要求的結果 (日記)
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (转)mysql使用Navicat 导出和导入数据库
  • (转载)Google Chrome调试JS
  • .NET C# 使用 SetWindowsHookEx 监听鼠标或键盘消息以及此方法的坑
  • .Net 代码性能 - (1)
  • .net 无限分类
  • .NetCore项目nginx发布
  • .net打印*三角形
  • .Net高阶异常处理第二篇~~ dump进阶之MiniDumpWriter