C++多线程:异步操作std::async和std::promise
文章目录
- std::async
- 简介
- 使用案例
- std::promise
- 简介
- 成员函数
- 总结
之前的文章中提到了C++多线程中的异步操作机制 C++ 多线程:future 异步访问类(线程之间安全便捷的数据共享),接下来分享关于异步操作中
async
和 promise
的相关使用总结。std::async
简介
- 头文件
<future>
- 使用方式
st::async( Function&& f, Args&&... args );
或者async( std::launch policy, Function&& f, Args&&... args );
- 简介
一般它的执行方式为我们以上说的两种
a. 第一种就是参数为函数名称以及需要传入的函数参数,此时async会开出对应行函数线程,并使用的默认的策略方式std::launch::async | std::launch::deferred
,这种策略标示async
产生的线程有两种执行方式:一种为线程独立执行,另一种为当主线程或者调用者线程中执行std::future::get
的成员函数时会执行产生的线程。
b. 另一种执行方式为显示声明执行策略policy ,以参数 args 调用函数 f
若设置 async 标志(即std::async(std::launch::async,f,x)
形式),则在async初始化所有线程局域对象之后会执行函数f。
若设置的是deferred标志(即std::async(std::launch::deferred,f,x)
),则async同样会使用std:thread
构造函数的方式转换f
和args
参数,但是并不会产生执行线程。此时它会进行惰性求值,即当async函数返回的std::future
对象进行get
取值的时候才会执行线程获取结果。 - 返回值
返回std::async
所创建的共享状态的对象std::future
使用案例
查看如下代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <future>
#include <string>
#include <mutex>std::mutex m;
struct X {void foo(int i, const std::string& str) {std::lock_guard<std::mutex> lk(m);std::cout << str << ' ' << i << '\n';}void bar(const std::string& str) {std::lock_guard<std::mutex> lk(m);std::cout << str << '\n';}int operator()(int i) {std::lock_guard<std::mutex> lk(m);std::cout << i << '\n';return i + 10;}
};template <typename RandomIt>
int parallel_sum(RandomIt beg, RandomIt end)
{auto len = end - beg;if (len < 1000)return std::accumulate(beg, end, 0);RandomIt mid = beg + len/2;auto handle = std::async(std::launch::async,parallel_sum<RandomIt>, mid, end);int sum = parallel_sum(beg, mid);return sum + handle.get();
}int main()
{std::vector<int> v(10000, 1);std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n';X x;// 以默认策略调用 x.foo(42, "Hello") :// 可能同时打印 "Hello 42" 或延迟执行,这里策略默认是std::launch::async|std::launch::deferred//即a1的打印可能在主线程打印的任何时候auto a1 = std::async(&X::foo, &x, 42, "Hello");// 以 deferred 策略调用 x.bar("world!")// 调用 a2.get() 或 a2.wait() 时打印 "world!"auto a2 = std::async(std::launch::deferred, &X::bar, x, "world!");// 以 async 策略调用 X()(43) :// 同时打印 "43"auto a3 = std::async(std::launch::async, X(), 43);a2.wait(); // 打印 "world!"std::cout << a3.get() << '\n'; // 打印 "53"
} // 若 a1 在此点未完成,则 a1 的析构函数在此打印 "Hello 42"
因为a1可能是立即执行,也有可能是惰性求值,所以a1对象的打印可能遍布整个打印的不同时间段。
a2则是惰性求值,当调用a2.wait()
或者a2.get()
求值的时候会获取a2的函数返回值
a3同样为异步求值,同时也能够支持get,只是get()会晚于线程执行之后。
输出如下:
第一种
he sum is 10000
world!
Hello 42
43
53
第二种
The sum is 10000
43
Hello 42
world!
53
第三种
The sum is 10000
Hello 42
43
world!
53
std::promise
简介
- 头文件
<future>
- 定义
template< class R > class promise
空模版
template< class R > class promise<R&>
非void特化,用于线程之间交流对象
template<> class promise<void>
用于交流无状态事件 - 简介
类模板 std::promise 提供存储值或异常的处理措施,之后通过 std::promise 对象所创建的 std::future 对象异步获得结果。注意 std::promise 只应当使用一次。
每个 promise 与共享状态关联,共享状态含有一些状态信息和可能仍未求值的结果,其中promise
对共享状态做三件事:- 就绪: promise 存储结果或异常于共享状态。标记共享状态为就绪,并解除阻塞任何等待于与该共享状态关联的 future 上的线程。
- 释放: promise 放弃其对共享状态的引用。若这是最后一个这种引用,则销毁共享状态。除非这是 std::async 所创建的未就绪的共享状态,否则此操作不阻塞。
- 抛弃: promise 存储以 std::future_errc::broken_promise 为 error_code 的 std::future_error 类型异常,令共享状态为就绪,然后释放它
成员函数
构造函数
a.promise();
默认构造函数,构造一个共享状态为空的 std::promise
b.template< class Alloc > promise( std::allocator_arg_t, const Alloc& alloc )
构造一个共享状态为空的 std::promise,由 alloc 分配共享状态
c.promise( promise&& other ) noexcept
移动构造函数,用原属 other 的共享状态构造新的 std::promise 对象,使用移动语义。构造完毕后, other 无共享状态;
d.promise( const promise& other ) = delete;
std::promise 不可复制析构函数
~promise()
两种情况调用析构函数
a.若共享状态就绪,则释放它。
b.若共享状态未就绪,则存储以 std::future_errc::broken_promise 为 error_condition 的 std::future_error 类型异常对象,令共享状态就绪再释放它。赋值运算符
promise& operator=( promise&& other ) noexcept
移动赋值运算符。首先析构原共享状态,然后如同以执行std::promise(std::move(other)).swap(*this)
对共享状态赋
promise& operator=( const promise& rhs ) = delete
std::promise 不可复制赋值std::promise<R>::get_future
返回与 *this 关联同一状态的 future 对象std::promise<R>::set_value
更新 promise 对象时获得单个与 promise 对象关联的互斥量, 若无共享状态或共享状态已存储值或异常,则抛出异常。对此函数的调用和对 get_future 的调用不会造成数据竞争。
查看如下代码,此时并不会造成对共享变量对竞争。#include <iostream> #include <thread> #include <future> #include <mutex>using namespace std;int fun1(std::future<int> &f) {int res = 1;int n = f.get();for (int i = n; i>1; --i) {res *=i;}cout << "Result is " << res << endl;return res; }int main() {int x;//std::thread t1(fun1,4,std::ref(x));//t1.join();std::promise<int> p;//标示f是一个需要从未来获取数值future对象std::future<int> f = p.get_future(); std::future<int> fu = std::async(std::launch::async,fun1, std::ref(f));//为f设置数值,在子线程中进行f.get()获取主线程到数值p.set_value(4);x = fu.get();cout << "Get from child " << x << endl;return 0; }
输出如下:
Result is 24 Get from child 24
std::promise<R>::set_value_at_thread_exit
原子地存储 value 到共享状态,而不立即令状态就绪。在当前线程退出时,销毁所有拥有线程局域存储期的对象后,再令状态就绪。即当创建的线程执行结束之前设置共享变量返回调用线程。
代码如下:#include <iostream> #include <future> #include <thread>int main() {//using namespace std::chrono_literals;std::promise<int> p;std::future<int> f = p.get_future();//在线程执行结束要离开之前设置共享状态,设置之前等待1s//并返回给线程调用者主线程中的f.wait获取值std::thread([&p] {std::this_thread::sleep_for(std::chrono::seconds(1));p.set_value_at_thread_exit(9);}).detach();std::cout << "Waiting..." << std::flush;f.wait();std::cout << "Done!\nResult is: " << f.get() << '\n'; }
输出如下:
Waiting...Done! Result is: 9
std::promise<R>::set_exception
存储异常指针 p 到共享状态中,并令状态就绪
主要用来进行线程异常情况的存储,同时将异常情况传出到调用线程进行处理
如下代码#include <thread> #include <iostream> #include <future>int main() {std::promise<int> p;std::future<int> f = p.get_future();std::thread t([&p]{try {// 可能抛出的代码throw std::runtime_error("Example");} catch(...) {try {// 存储任何抛出的异常于 promise,设置异常值到共享状态并传出p.set_exception(std::current_exception());} catch(...) {} // set_exception() 亦可能抛出}});try {//获取一异常的共享状态std::cout << f.get();} catch(const std::exception& e) {std::cout << "Exception from the thread: " << e.what() << '\n';}t.join(); }
输出如下:
Exception from the thread: Example
std::promise<R>::set_exception_at_thread_exit
存储异常指针 p 到共享状态中,而不立即使状态就绪。在当前线程退出时,销毁所有拥有线程局域存储期的变量后,再零状态就绪。
总结
async
提供异步操作,可以支持线程异步执行或者惰性求值来达到对线程执行情况以及共享变量获取时机的控制。即我想要获取变量,使用async
的std::launch::deferred
让线程future对象想要获取线程函数结果时再进行线程的执行返回,在此期间线程函数即可处于休眠,依此提供异步线程共享变量机制。
而promise
类则提供一种共享状态的访问机制,多线程之间的状态共享可以通过promise
类对象的get_future
监控数据的共享状态,set_value
以及set_value_exit
等成员函数设置数据的共享状态且内部保证不会产生对共享数据的竞争问题,依此来提供安全使用便捷的多线程之间数据的访问机制。
相关文章:

5如何将表格的一行数据清空_微信公众号推文中如何自定义添加表格?
微信公众号发的图文消息里经常需要有表格,因为表格进行数据展现更直观明了,所以接下来就给大家分享一下如何自定义添加表格。打开小蚂蚁编辑器,在编辑区点击鼠标右键,在出现的选项中选择【插入表格】。系统会默认添加一个5行5列的…

基于visual Studio2013解决面试题之0901奇偶站队
题目解决代码及点评/*给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数 解决方法:两边同时遍历,如果遇到左边偶数或者右边基数,则交换 */#include <iostrea…

联想架构调整:智能手机业务很重要
1月5日,杨元庆宣布联想集团进行组织架构调整。将联想旗下业务拆分为两个新的端到端业务集团—— Lenovo 业务集团和 Think 业务集团。杨元庆在内部邮件如是说:“Lenovo业务集团(Lenovo Business Group,简称LBG),由刘军领军,致力于…

activiti5/6 系列之--BpmnModel使用
BpmnModel对象,是activiti动态部署中很重要的一个对象,如果BpmnModel对象不能深入的理解,那可能如果自己需要开发一套流程设计器,使用bpmn-js使用前端或者C/S展现流程流转而不是使用工作流引擎,就显得力不从心。例如&a…

C++多线程:package_task异步调用任何目标执行操作
文章目录描述函数成员及使用总结我们上一篇描述关于C多线程中的异步操作相关库( async和 promise),本节将分享c标准库中最后一个多线程异步操作库 package_task的学习笔记。描述 头文件 <future> 声明方式: template< class R, class ...Args > class …

ICE BOX 配置,使用----第一篇
一 理论部分 (1) 为什么要使用icebox? icebox server代替了通常的server. icebox是为了方便集中管理多个ice服务而建立的。 它通过使用icebox服务器,把ice服务注册进去,从而建立联系。 所以它除了建立传统的ice服务器,ice客户端,…

测试打桩_DNF:CEO实测旭旭宝宝红眼,打桩高达2494E,伤害超越狂人剑魂
对于红眼这个职业,旭旭宝宝倾注了太多的心血,耗资几千万打造而成。虽然,作为固伤职业,在伤害方面,不及剑魂和剑帝这类百分比,但因人数颇多,从而被广泛关注。而今,旭旭宝宝的红眼&…

122112_1452_Word1
122112_1452_Word1转载于:https://www.cnblogs.com/joshuali/archive/2013/01/11/4339303.html

C++ 多线程:时间控制
C多线程库中的各个子库都有各自的时间控制方式,依此来进行多线程程序运行中cpu资源的精确控制。 使用std::chrono时间库可以提供微妙、毫秒、秒及以上的时间取用,并且能够获取当前系统时间。 如下代码 #include <iostream> #include <fstream> #inclu…

array用法 numpy_NumPy总结(基础用法)
numpy可以说是Python运用于人工智能和科学计算的一个重要基础,近段时间恰好学习了numpy,pandas,sklearn等一些Python机器学习和科学计算库,因此在此总结一下常用的用法。引入numpy库的方式:import numpy as np1、numpy…

python 字符串 转 dict
比直接eval更好的方法>>>import ast >>>ast.literal_eval("{muffin : lolz, foo : kitty}") {muffin:lolz,foo:kitty} 用 json 遇到问题: >>> import json json.loads({"x": 1,"y":2}) {uy: 2, ux: 1…

rhino-java中调用javascript
2019独角兽企业重金招聘Python工程师标准>>> 在有些情况下需要在java里面执行javascript,这时rhino就可以帮忙了。mozilla的一个开源产品。 官网https://developer.mozilla.org/en-US/docs/Rhino 之前的一篇博客http://my.oschina.net/yybear/blog/101…

HDU 2566 统计硬币
统计硬币 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 9633 Accepted Submission(s): 6505 Problem Description 假设一堆由1分、2分、5分组成的n个硬币总面值为m分,求一共有多少种可能的组合方式&…

linux C 多线程编程
文章目录多线程的一些小知识:1创建线程 pthread_create2线程挂起 pthread_join3线程终止 pthread_exit4线程分离 pthread_detach5线程取消 pthread_cancel线程同步 pthread_mutex_t互斥变量我们在写linux的服务的时候,经常会用到linux的多线程技术以提高…

JavaSE replaceAll 方法
private String srcStr "index\\.php\\?action";//要替换的原字符串 private String destStr "index.php?<{\\$LANGUAGE_TYPE}>action";//目的字符串 注意.和?都要转义,因此需要在前面添加两个反斜杠。 关于第二个参数,$…

elementui datetimepicker 移动端_在 Gitee 收获 2.5K Star,前后端分离的 RuoYi 它来了
作为 2019 年 Gitee 上最受欢迎的开源项目,权限管理系统 RuoYi 已经在 Gitee 上获得了超过 11K 的 Star。 这次作者若依推出了基于 SpringBoot Vue Element UI 的前后端分离版本的 RuoYi-Vue,方便有前后端分离开发需求的同学使用。项目名称:…

用MyEclipse开发Spring入门
1 新建一个项目 File----->New ----->Project 在出现的对话框中选择 MyEclipse 下的 Web Project,在Project Name 输入mySpring1,其他的选项默认,再点Finish完成; 2 加入Spring 包 在myspring1 项目上点右键,选…

带你轻而易举的学习python——八皇后问题
首先我们来看一下这个著名的八皇后问题 八皇后问题:在88格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 在这个问题提出之后人们又将它扩展到了nn格的棋盘摆…

ubuntu18.04.1内核升级至5.0.0-25版本
ubuntu18.04操作系统版本先已支持在线的内核版本升级,到目前为止18.04发布版已经拥有三个小版本了1,2,3。 其中18.04.01和18.04.03版本,安装好之后默认的是4.15内核版本,但是默认支持在线安装4.18和5.0.0内核版本。 具体升级步骤如下&#x…

输出n行杨辉三角数
1 /*2 输出n行杨辉三角数 3 输入n,n是1~100之间的整数 4 */5 #include<stdio.h>6 int main()7 {8 int a[100],b[100];9 int i,j; 10 int n; 11 scanf("%d",&n); 12 if(n1) 13 { 14 printf("1\…

怎么扫描_打印机上扫描仪怎么用 打印机上扫描仪使用及添加方法
打印机是生活中常用的打印设备,主要用于连接 电脑 打印电脑上的文件,方便办公。对于第一次使用打印机的朋友可能还不是很熟悉如何使用,比如打印机上 扫描仪 怎么用?怎么添加打印机扫描仪?下面小编就来为大家介绍下吧。…

java 调用webservice的各种方法总结
http://www.blogjava.net/zjhiphop/archive/2009/04/29/webservice.html 现在webservice加xml技术已经逐渐成熟,但要真正要用起来还需时日!! 由于毕业设计缘故,我看了很多关于webservice方面的知识,今天和大家一起来研究研究webservice的…

vc++图像保存,重绘
新建mfc应用程序,单文档 增加绘图 分别增加命令响应 添加成员变量UINIT 图形可以运行,如何保存呢?(一个集合类,CPtArt) 用一个类的对象来保存一个图形的三个要素 所以插入一个新的类(通常的类&a…

linux 进程内存分布及 堆分配和栈分配的特点
文章目录进程内存空间分布size命令查看内存分布堆方式内存分配和栈方式内存分配比较使用stap 深入追踪malloc逻辑进程内存空间分布 一个程序的内存空间主要如下: 代码段(text segment):只读权限;常是指用来存放程序执行代码的一块内存区域&…

echarts 坐标自适应_echarts 同一页面,多个图表 页面大小自适应
// 路径配置require.config({paths: {echarts: ./js}});// 使用require([echarts,echarts/chart/line, // 折线图echarts/chart/bar // 柱状图],function (ec) {var myChart ec.init(document.getElementByIdx_x(main));var myChartx ec.init(document.getElementByIdx_x(main…

opencv——pcb上寻找mark点(拟合椭圆的方法)
#include "stdafx.h" // FitCircle.cpp : 定义控制台应用程序的入口#include "cv.h" #include "highgui.h" #include "cxcore.h" #include "cvaux.h" #include <iostream>using namespace cv; using namespace std; v…

bit,Byte、KB、MB、GB、TB、PB、EB之间的关系
计算机存储信息的最小单位,称之为位(bit),音译比特,二进制的一个“0”或一个“1”叫一位;计算机存储容量基本单位是字节(Byte),音译为拜特,8个二进制位组成1个…

echarts 点亮中国插件研究
echarts 真的是个神奇的东西,最近做了一个需要点亮中国的移动端项目,前期就怎样点亮中国做了调研,发现微博当初炫酷的点亮效果就是用echarts做的,于是研究了一下。 一连研究了一堆demo,不管从官网还是GitHub上面&#…

linux进程间通信:无名管道 pipe
文章目录内核层实现结构通信原理特点使用函数声明使用实例单向通信双向通信编程注意事项管道中无数据时读操作会阻塞将管道的写端句柄关闭,不会影响读端数据读取管道中没有数据,写操作关闭则读操作会立即返回管道大小测试 64K管道发生写满阻塞࿰…

争吵所达到的效果要_悟空:不要害怕争吵,有时候争吵一些不喜欢的事情也能创造和谐...
悟空:八戒,你吃了早饭去把马喂了吧。八戒:好的。悟空:喂了马你去看看我们午饭可以吃什么,如果有需要提前做预备的什么事儿,你知道该怎么做吧?八戒:好的。悟空:昨天&#…