文章目录

  • std::async
    • 简介
    • 使用案例
  • std::promise
    • 简介
    • 成员函数
  • 总结

之前的文章中提到了C++多线程中的异步操作机制 C++ 多线程:future 异步访问类(线程之间安全便捷的数据共享),接下来分享关于异步操作中 asyncpromise的相关使用总结。

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构造函数的方式转换fargs参数,但是并不会产生执行线程。此时它会进行惰性求值,即当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提供异步操作,可以支持线程异步执行或者惰性求值来达到对线程执行情况以及共享变量获取时机的控制。即我想要获取变量,使用asyncstd::launch::deferred让线程future对象想要获取线程函数结果时再进行线程的执行返回,在此期间线程函数即可处于休眠,依此提供异步线程共享变量机制。

promise类则提供一种共享状态的访问机制,多线程之间的状态共享可以通过promise类对象的get_future监控数据的共享状态,set_value以及set_value_exit等成员函数设置数据的共享状态且内部保证不会产生对共享数据的竞争问题,依此来提供安全使用便捷的多线程之间数据的访问机制。

C++多线程:异步操作std::async和std::promise相关推荐

  1. C++多线程std::async、std::future、std::packaged_task、std::promise

    std::async std::async用于创建异步任务,实际上就是创建一个线程执行相应任务,默认立即开始执行. std::async就是异步编程的高级封装,std::async的操作,其实相当于封 ...

  2. C++——异步操作(std::future、std::async、std::packaged_task、std::promise)

    文章目录 一.std::future与std::async 1.基本概念 2.代码演示 二.std::future与std::packaged_task 1.基本知识 2.代码演示 三.std::fu ...

  3. C++——std::async和std::thread

    作者:小 琛 欢迎转载,请标明出处 参考文章: 傻月菇凉博主-博客 OceanStar的学习笔记博主-博客 NGC_2070博主-博客 文章目录 std::thread thread的提出 使用方法. ...

  4. C++多线程编程(3) 异步操作类 std::future std::promise std::async

    C++中提供了异步操作相关的类: 1. std::future: 异步结果的传输通道,可以很方便的获取线程函数的返回值. 在C++中,如果希望获取线程函数的返回值,就不能直接通过thread.join ...

  5. std::future、std::promise、std::packaged_task、std::async

    #include <iostream> #include <string> #include <future> #include <thread> #i ...

  6. std::future和std::promise和std::packaged_task

    std::future 其实future有两个兄弟,一个是std::future, 一个是它大哥std::shared_future.他们的区别就是std::future只支持移动语义,它所引用的共享 ...

  7. std::atomic、std::async深入研究

    1.从上节课的一个demo说起. #include <iostream> #include <future> #include <vector> #include ...

  8. C++并发编程之std::async(), std::future, std::promise, std::packaged_task

    c++11中增加了线程,使得我们可以非常方便的创建线程,它的基本用法是这样的: void f(int n); std::thread t(f, n + 1); t.join(); 但是线程毕竟是属于比 ...

  9. C++11多线程------std::async

    std::async可以认为是封装了一个std::promise,该函数返回一个std::future,用于获取其他线程的数据. 该函数的本质是内部封装构造了一个线程,然后传递给一个std::prom ...

最新文章

  1. centos7.0 lamp mysql_CentOS7 yum安装LNMP以及LAMP
  2. stm32cubeide烧写程序_stm32mp157 Cortex M4开发篇:stm32CubeIDE开发环境搭建
  3. Ribbon之ServerList
  4. c语言不能在函数中求数组大小,C语言中数组长度不能用变量定义吗?
  5. Matplotlib 中文用户指南 1 简介
  6. Vhost and VIOMMU
  7. CENTOS7.8忘记ROOT密码,重置密码步骤
  8. Cent OS防火墙配置端口开放
  9. POJ读书笔记2.1 —— 鸡兔笼带
  10. day21-2 类的派生
  11. Noteexpress更新文献时总出现重复的文献----有人知道怎么解决吗?请多多指教!
  12. 网站中引入百度地图的方法分享(含源码)
  13. 中间代码生成-四元式
  14. Python程序设计基础(第九章 字典和集合 练习记录)
  15. 【实习日报】2019年3月 前端开发实习工作日报汇总
  16. Tableau5——地图,仪表板
  17. 机器学习学习笔记-多项式中的过拟合,泛化能力等
  18. ATM机多账号的实现
  19. C#读取QQWry.Dat文件实现IP查询
  20. 通过NFS服务器将设备目录挂载到Windows目录

热门文章

  1. 金额阿拉伯数字转换为中文大写
  2. 九度oj 题目1411:转圈
  3. VS2010 编译 QT4.8.7 x64
  4. PowerShell 2.0 实践(十二)管理 SQL Server 2008 R2(1)
  5. 计算机有什么创新,笔记本创新技术有哪些?【详解】
  6. linux fork 目录,linux fork()理解
  7. php编译7教程,PHP7 快速编译安装
  8. Linux多线程的同步------读写锁
  9. labview简易计算机实验报告,labview实验报告..doc
  10. java datasource mysql_Java MysqlDataSource類代碼示例