c++ future与promise

C++11 标准中 头文件中包含了以下几个类和函数:

  • Providers 类:std::promise, std::package_task
  • Futures 类:std::future, shared_future.
  • Providers 函数:std::async()
  • 其他类型:std::future_error, std::future_errc, std::future_status, std::launch.

C++11中promise和future机制是用于并发编程的一种解决方案,用于在不同线程完成数据传递(异步操作)

std::promise

std::promise 对象可以保存某一类型 T 的值,该值可被 future 对象读取(可能在另外一个线程中),因此 std::promise 提供了一种线程同步的手段。在 std::promise 对象构造时可以和一个共享状态(通常是std::future)相关联,并可以在相关联的共享状态(std::future)上保存一个类型为 T 的值。

可以通过 get_future 来获取与该 std::promise 对象相关联的 std::future 对象,调用该函数之后,两个对象共享相同的共享状态。

  • std::promise 对象是异步 Provider,它可以在某一时刻设置共享状态的值。
  • std::future 对象可以异步返回共享状态的值,或者在必要的情况下阻塞调用者并等待共享状态标志变为 ready,然后才能获取共享状态的值。
函数作用
operator=从另一个 std::promise 移动到当前对象。
swap()交换移动两个 std::promise。
get_future()获取与其管理的std::future
set_value()设置共享状态值,此后promise共享状态标识变为ready
set_value_at_thread_exit()设置共享状态的值,但是不将共享状态的标志设置为 ready,当线程退出时该 promise 对象会自动设置为 ready
set_exception()设置异常,此后promise的共享状态标识变为ready
set_exception_at_thread_exit()设置异常,但是到该线程结束时才会发出通知。

std::promise::get_future

该函数返回一个与 promise 共享状态相关联的 future 。返回的 future 对象可以访问由 promise 对象设置在共享状态上的值或者某个异常对象。只能从 promise 共享状态获取一个 future 对象。在调用该函数之后,promise 对象通常会在某个时间点准备好(设置一个值或者一个异常对象),如果不设置值或者异常,promise 对象在析构时会自动地设置一个 future_error 异常(broken_promise)来设置其自身的准备状态。

#include <iostream>       
#include <functional>     
#include <thread>        
#include <future>     // std::promise, std::futurevoid print_int(std::future<int>& fut) {int x = fut.get();                    // 获取共享状态的值.std::cout << "value: " << x << '\n';  // 打印 value: 10.
}int main ()
{std::promise<int> prom;                    // 生成一个 std::promise<int> 对象.std::future<int> fut = prom.get_future();  // 和 future 关联.std::thread t(print_int, std::ref(fut));   // 将 future 交给另外一个线程t.prom.set_value(10);                        // 设置共享状态的值, 此处和线程t保持同步.t.join();return 0;
}

std::promise 构造函数

std::promise 的 operator= 没有拷贝语义,即 std::promise 普通的赋值操作被禁用,operator= 只有 move 语义,所以 std::promise 对象是禁止拷贝的。

#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <future>         // std::promise, std::futurestd::promise<int> prom;void print_global_promise () {std::future<int> fut = prom.get_future();int x = fut.get();std::cout << "value: " << x << '\n';
}int main ()
{std::thread th1(print_global_promise);prom.set_value(10);th1.join();prom = std::promise<int>();    // prom 被move赋值为一个新的 promise 对象.std::thread th2 (print_global_promise);prom.set_value (20);th2.join();return 0;
}

std::promise::set_exception

为 std::promise 设置异常,此后 std::promise 的共享状态变标志变为 ready,例子如下,线程1 从终端接收一个整数,线程 2 将该整数打印出来,如果线程 1 接收一个非整数,则为 std::promise 设置一个异常 ,线程 2 在 std::future::get 是抛出该异常。

#include <iostream>      
#include <functional>    
#include <thread>        
#include <future>         
#include <exception>    // std::exception, std::current_exceptionvoid get_int(std::promise<int>& prom) {int x;std::cout << "Please, enter an integer value: ";std::cin.exceptions (std::ios::failbit);   // throw on failbittry {std::cin >> x;                         // sets failbit if input is not intprom.set_value(x);} catch (std::exception&) {prom.set_exception(std::current_exception());}
}void print_int(std::future<int>& fut) {try {int x = fut.get();std::cout << "value: " << x << '\n';} catch (std::exception& e) {std::cout << "[exception caught: " << e.what() << "]\n";}
}int main ()
{std::promise<int> prom;std::future<int> fut = prom.get_future();std::thread th1(get_int, std::ref(prom));std::thread th2(print_int, std::ref(fut));th1.join();th2.join();return 0;
}

std::promise::set_value_at_thread_exit

设置共享状态的值,但是不将共享状态的标志设置为 ready,当线程退出时该 promise 对象会自动设置为 ready。如果某个 std::future 对象与该 promise 对象的共享状态相关联,并且该 future 正在调用 get,则调用 get 的线程会被阻塞,当线程退出时,调用 future::get 的线程解除阻塞,同时 get 返回 set_value_at_thread_exit 所设置的值。注意,该函数已经设置了 promise 共享状态的值,如果在线程结束之前有其他设置或者修改共享状态的值的操作,则会抛出 future_error( promise_already_satisfied )。

std::future

  • std::future仅在创建它的std::promise(或者std::async、std::packaged_task)有效时才有用,所以可以在使用前用valid()判断
  • std::future可供异步操作创建者用各种方式查询、等待、提取需要共享的值,也可以阻塞当前线程等待到异步线程提供值。
  • std::future一个实例只能与一个异步线程相关联,多个线程则需要使用std::shared_future。
函数作用
operator=移动 future 对象,移动!
share()返回一个可在多个线程中共享的 std::shared_future 对象。
get()获取值(类型由模板类型决定)
valid()检查 future 是否处于被使用状态,也就是它被首次在首次调用 get() 或 share() 前。建议使用前加上valid()判断
wait()阻塞等待调用它的线程到共享值成功返回。
wait_for()在规定时间内 阻塞等待调用它的线程到共享值成功返回。
wait_until()在指定时间节点内 阻塞等待调用它的线程到共享值成功返回。

Promise和Future模型

std::future负责访问, std::future是一个模板类,它提供了可供访问异步执行结果的一种方式。
std::promise负责存储, std::promise也是一个模板类,它提供了存储异步执行结果的值和异常的一种方式。
std::future负责访问,std::promise负责存储,同时promise是future的管理者

流程:

  1. 线程1初始化一个promise和future对象,将promise对象传递给线程2,相当于线程2对线程1的一个承诺
  2. future相当于一个承诺,用于获取未来线程2的值
  3. 线程2接受一个promise,需要将处理结果通过promise返回给线程1
  4. 线程1想要获取数据,此时线程2还未返回promise就阻塞等待处,直到线程2的数据可达

生产-消费模型

mutex + condition_variable
为了不浪费系统资源,我们使用条件变量来使 process_thread 即消费线程进入休眠等待,直到数据准备好了然后通知它来处理。这里为了在线程间进行数据传递,我们需要一个全局变量 data,然后利用锁和条件变量这些机制来保证共享数据的安全。

#include <thread>
#include <mutex>
#include <iostream>
#include <chrono>
#include <memory>
#include <condition_variable>struct _data
{bool ready;int32_t value;
};_data data = { false, 0 };
std::mutex data_mutex;
std::condition_variable data_con;int main()
{//生产数据的线程std::thread prepare_data_thread([](){std::this_thread::sleep_for(std::chrono::seconds(2));    //模拟生产过程, std::unique_lock<std::mutex> ulock(data_mutex);data.ready = true;data.value = 1;data_con.notify_one();});//消费数据的线程std::thread process_data_thread([](){std::unique_lock<std::mutex> ulock(data_mutex);cv.wait(ulock, [](){ return data.ready; });std::cout << data.value << std::endl;});prepare_data_thread.join();process_data_thread.join();system("pause");return 0;
}

promise+future
首先创建一个 _data 类型data_promise ,而在 data_promise 里已经封装好了一个 _data类型的future,调用 promise 的 get_future() 方法得到与之对应的 future。把 data_promise 传递给了 prepare_data_thread,便可以在 prepare_data_thread 里面来产出值了,在休眠2S之后,调用了 set_value() 方法来产出值。将和 data_promise 相关联的 data_future 传递给了 process_data_thread,所以便可以在 process_data_thread 里调用 data_future 的 get() 方法获取 data_promise 的产出值。这里需要注意的一点是,future 的 get() 方法是阻塞的,所以在与其成对的 promise 还未产出值,也就是未调用 set_value() 方法之前,调用 get() 的线程将会一直阻塞在 get()处直到其他任何人调用了 set_value() 方法。

#include <thread>
#include <iostream>
#include <future>
#include <chrono>struct _data
{int32_t value;
};_data data = { 0 };int main()
{std::promise<_data> data_promise;      //创建一个承诺std::future<_data> data_future = data_promise.get_future();     //得到这个承诺封装好的期望std::thread prepare_data_thread([](std::promise<_data> &data_promise){std::this_thread::sleep_for(std::chrono::seconds(2));    //模拟生产过程data_promise.set_value({ 1 });       //通过set_value()反馈结果}, std::ref(data_promise));std::thread process_data_thread([](std::future<_data> &data_future){std::cout << data_future.get().value << std::endl;    //通过get()获取结果}, std::ref(data_future));prepare_data_thread.join();process_data_thread.join();system("pause");return 0;
}

packaged_task
packaged_task 是对一个任务的抽象,可以给其传递一个函数来完成其构造。相较于 promise,它应该算是更高层次的一个抽象,同样地,可以将任务投递给任何线程去完成,然后通过 packaged_task.get_future() 方法获取的 future 来获取任务完成后的产出值。

packaged_task 也是一个类模板,模板参数为函数签名,也就是传递函数的类型,如上例中为 _data(),返回值为 _data 类型,函数参数为 void,其中返回值类型将决定 future 的类型也就是产出值类型。
创建一个任务,并投递给了 do_task_thread 去完成这个任务,然后将对应的 data_future投递 给了process_data_thread ,就可以在 process_data_thread 里获取任务产出值了。同样地,获取值之前必须等待任务的完成。

#include <thread>
#include <iostream>
#include <future>struct _data
{int32_t value;
};_data data = { 0 };int main()
{std::packaged_task<_data()> prepare_data_task([]()->_data{std::this_thread::sleep_for(std::chrono::seconds(2));    //模拟数据生产过程return{ 1 };});auto data_future = prepare_data_task.get_future();          //获取futurestd::thread do_task_thread([](std::packaged_task<_data()> &task){task();              //调用packaged_task的调用符来运行任务}, std::ref(prepare_data_task));std::thread process_data_thread([](std::future<_data> &data_future){std::cout << data_future.get().value << std::endl;}, std::ref(data_future));do_task_thread.join();process_data_thread.join();system("pause");return 0;
}

async
async 返回一个与函数返回值相对应类型的 future,通过它我们可以在其他任何地方获取异步结果。
由于给 async 提供了 std::launch::async 策略,所以生产过程将被异步执行,具体执行的时间取决于各种因素,最终输出的时间为 2000ms+,可见生产过程和主线程是并发执行的。

除了 std::launch::async,还有一个 std::launch::deferred 策略,它会延迟线程地创造,也就是说只有当调用 future.get() 时子线程才会被创建以执行任务。

#include <thread>
#include <iostream>
#include <chrono>
#include <future>struct _data
{int32_t value;
};_data data = { 0 };int main()
{auto start = std::chrono::steady_clock::now();std::future<_data> data_future = std::async(std::launch::async, []()->_data{std::this_thread::sleep_for(std::chrono::seconds(1));           //模拟生产过程return { 1 };});std::this_thread::sleep_for(std::chrono::seconds(2));std::cout << data_future.get().value << std::endl;              //使用产出值auto end = std::chrono::steady_clock::now();std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << std::endl;system("pause");return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/56369.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sleep () 和 wait () 有什么区别?

sleep () 方法 sleep () 方法是Thread类的静态方法&#xff0c;可以通过Thread.sleep()来调用。 它是线程的一个静态方法&#xff0c;用于让当前线程休眠一段指定的时间。 sleep () 不会释放线程持有的锁&#xff0c;因此其他线程无法获得锁资源&#xff0c;可能导致同步问题…

13.4 目标检测锚框标注 非极大值抑制

锚框的形状计算公式 假设原图的高为H,宽为W 锚框形状详细公式推导 以每个像素为中心生成不同形状的锚框 # s是缩放比&#xff0c;ratio是宽高比 def multibox_prior(data, sizes, ratios):"""生成以每个像素为中心具有不同形状的锚框"""in_he…

后端Windows软件环境安装配置大全[JDK、Redis、RedisDesktopManager、Mysql、navicat、VMWare、finalshell、MongoDB...持续更新中]

文章目录 前言1. 安装 JDK2. 安装 Redis3. 安装 RedisDesktopManager&#xff08;Redis可视化工具&#xff09;4. 安装 Mysql5. 安装 navicat (Mysql可视化工具)6. 安装 VMWare7. 安装 finalshell (VMWare可视化工具)8. 安装 MongodDB9. 安装 maven 总结 前言 为了巩固所学的知…

vue 主组件把日期选择器给子组件props传obj值, 与子组件监听 watch对象或对象属性

1 主组件 1.1 :passObj 这种 非v-model ; change"DateChange"触发事件 <template> <div class"date-picker-panel"><el-date-picker v-model"value2" type"datetimerange" :picker-options"pickerOptions"…

Web安全测试(三):SQL注入漏洞

一、前言 结合内部资料&#xff0c;与安全渗透部门同事合力整理的安全测试相关资料教程&#xff0c;全方位涵盖电商、支付、金融、网络、数据库等领域的安全测试&#xff0c;覆盖Web、APP、中间件、内外网、Linux、Windows多个平台。学完后一定能成为安全大佬&#xff01; 全部…

WSL2 window上高效运行Linux

1 WSL及优势 1.1 WSL简介 WSL&#xff08;Windows Subsystem for Linux&#xff09;是Windows操作系统中的一个功能&#xff0c;它允许用户在Windows环境下运行Linux应用程序。WSL提供了一个与Linux内核兼容的系统调用转发层&#xff0c;使得Linux二进制文件可以在Windows上直…

基于风险的漏洞管理

基于风险的漏洞管理涉及对即将被利用的漏洞的分类响应&#xff0c;如果被利用&#xff0c;可能会导致严重后果。本文详细介绍了确定漏洞优先级时要考虑的关键风险因素&#xff0c;以及确保基于风险的漏洞管理成功的其他注意事项。 什么是基于风险的漏洞管理对基于风险的漏洞管…

vue中html引入使用<%= BASE_URL %>变量

首先使用src相对路径引入 注意&#xff1a; js 文件放在public文件下 不要放在assets静态资源文件下 否则 可能会报错 GET http://192.168.0.113:8080/src/assets/js/websockets.js net::ERR_ABORTED 500 (Internal Server Error) 正确使用如下&#xff1a;eg // html中引…

(线特征)opencv+opencv contribute 配置

写一篇博客&#xff0c;记录开始线特征slam的历程。 在配置环境的时候&#xff0c;可以发现大多数都是用到了opencv3.4.16和其contribute版本&#xff0c;这里进行一个相关操作的教学。配置环境是在Ubuntu下面进行的&#xff0c;建议使用Ubuntu18来进行线特征的配置以及代码的…

8月27日,每日信息差

1、大众集团在美因发动机故障召回2018辆汽车。发动机连杆轴承可能损坏&#xff0c;导致发动机故障和熄火&#xff0c;从而增加撞车风险。经销商将对发动机进行检查&#xff0c;必要时免费更换发动机&#xff08;财联社&#xff09; 2、赛百味确认出售协议达成。美国快餐连锁店…

国产AI芯片突破,芯片或成白菜价,恐惧的美芯阻止台积电为它代工

日前消息指台积电大幅减少一家中国AI芯片企业的产能&#xff0c;原因在于国产AI芯片的性能已接近美芯&#xff0c;美国芯片企业NVIDIA与相关的资本机构贝莱德联手施压台积电所致&#xff0c;凸显出美国芯片忧虑中国AI芯片的竞争力。 这家国产AI芯片企业为壁仞科技&#xff0c;据…

【精算研究01/10】 计量经济学的性质和范围

一、说明 计量经济学是使用统计方法来发展理论或测试经济学或金融学中的现有假设。计量经济学依赖于回归模型和零假设检验等技术。计量经济学也可以用来预测未来的经济或金融趋势。 图片来源&#xff1a;https://marketbusinessnews.com 二、 计量经济之简介 计量经济学是对经济…

定义和实施需求基线

在传统的瀑布开发模式中&#xff0c;开发人员通常在初始阶段就确定需求并将之“冻结”&#xff0c;以便在不受需求变更影响的情况下进行开发。然而&#xff0c;实际经验告诉我们&#xff0c;这种方法在多数情况下并不适用&#xff0c;更为有效的方法是先建立需求基线&#xff0…

Spring MVC 学习总结

学习目标 了解 Spring MVC 是什么&#xff0c;为什么要使用它或者说它能解决什么问题&#xff0c;其与 Spring 是什么关系。理解为什么配置 Spring MVC 的前端控制器的映射路径为 “/” 会导致静态资源访问不了&#xff0c;掌握怎么处理这个问题。掌握基于注解方式使用 Spring…

有哪些前端调试和测试工具? - 易智编译EaseEditing

前端开发调试和测试工具帮助开发人员在开发过程中发现和修复问题&#xff0c;确保网站或应用的稳定性和性能。以下是一些常用的前端调试和测试工具&#xff1a; 调试工具&#xff1a; 浏览器开发者工具&#xff1a; 现代浏览器&#xff08;如Chrome、Firefox、Safari等&#…

深度学习2.神经网络、机器学习、人工智能

目录 深度学习、神经网络、机器学习、人工智能的关系 大白话解释深度学习 传统机器学习 VS 深度学习 深度学习的优缺点 4种典型的深度学习算法 卷积神经网络 – CNN 循环神经网络 – RNN 生成对抗网络 – GANs 深度强化学习 – RL 总结 深度学习 深度学习、神经网络…

Hbase-技术文档-java.net.UnknownHostException: 不知道这样的主机。 (e64682f1b276)

问题描述&#xff1a; 在使用spring-boot操作habse的时候&#xff0c;在对habse进行操作的时候出现这个问题。。 报错信息如下&#xff1a; 第一段报错&#xff1a; 第二段报错&#xff1a; java.net.UnknownHostException: e64682f1b276 问题定位解读&#xff1a; 错误 ja…

Android 13 - Media框架(7)- NuPlayer::Source

Source 在播放器中起着拉流&#xff08;Streaming&#xff09;和解复用&#xff08;demux&#xff09;的作用&#xff0c;Source 设计的好坏直接影响到播放器的基础功能&#xff0c;我们这一节将会了解 NuPlayer 中的通用 Source&#xff08;GenericSource&#xff09;关注本地…

Nginx配置文件详解

Nginx配置文件详解 1、Nginx配置文件1.1主配置文件详解1.2子配置文件 2、全局配置部分2.1修改启动的工作进程数&#xff08;worker process) 优化2.2cpu与worker process绑定2.3 PID 路径修改2.4 修改工作进程的优先级2.5调试工作进程打开的文件的个数2.6关闭master-worker工作…

postman接口自动化测试框架实战!

什么是自动化测试 把人对软件的测试行为转化为由机器执行测试行为的一种实践。 例如GUI自动化测试&#xff0c;模拟人去操作软件界面&#xff0c;把人从简单重复的劳动中解放出来。 本质是用代码去测试另一段代码&#xff0c;属于一种软件开发工作&#xff0c;已经开发完成的用…