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

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;
}

相关文章:

c++ future与promise

C11 标准中 头文件中包含了以下几个类和函数&#xff1a; Providers 类&#xff1a;std::promise, std::package_taskFutures 类&#xff1a;std::future, shared_future.Providers 函数&#xff1a;std::async()其他类型&#xff1a;std::future_error, std::future_errc, st…...

在x86机器上的Docker运行arm64容器

1. 引言 工作中常用电脑主机CPU为x86架构&#xff0c;有时由于产品需要&#xff0c;我们需要编译aarch64架构的SDK或者应用程序供使用或者测试。 一种比较快捷的方式是使用aarch64的CPU构建相应操作系统&#xff0c;实现真机运行。但在无arm架构CPU环境下&#xff0c;我们可否…...

centos7删除乱码文件

centos7删除乱码文件1. 小白教程&#xff0c;一看就会&#xff0c;一做就成。 1.解释 当文件名为乱码的时候&#xff0c;无法通过键盘输入文件名&#xff0c;所以在终端下就不能直接利用rm&#xff0c;mv等命令管理文件了。 但是每个文件都有一个i节点号&#xff0c;可以通过…...

uni-app里使用webscoket

实现思路和vue中是一样的。如果想看思路可以看这篇文章&#xff1a;websocket 直接上可以运行的代码&#xff1a; 一、后端nodeJS代码&#xff1a; 1、新建项目文件夹 2、初始化项目&#xff1a; npm init -y 3、项目里安装ws npm i ws --save 4、nodeJS代码&#xff1…...

jdk17+springboot使用webservice,踩坑记录

这几天wms对接lbpm系统&#xff0c;给我的接口是webservice的&#xff0c;老实说&#xff0c;这个技术很早&#xff0c;奈何人家只支持这个。 环境说明&#xff1a;JDK17 springboot2.6.6。网上很多教程是基于jdk8的&#xff0c;所以很多在17上面跑不起来。折腾两天&#xff0c…...

计算机网络文件拆分—视频流加载、断点续传

视频流加载 视频流加载的原理是通过网络传输和播放器解码来实现的。 首先&#xff0c;视频文件会被分成一系列小的数据包&#xff0c;通常是以流的形式传输&#xff0c;这些数据包通过网络传输到用户设备。在传输过程中&#xff0c;可以采用各种协议&#xff0c;如HTTP、RTSP…...

JVM 给对象分配内存空间

指针碰撞空闲列表TLAB 为对象分配空间的任务实际上便等同于把一块确定大小的内存块从Java堆中划分出来。 指针碰撞&#xff1a;&#xff08;Bump The Pointer&#xff09; 堆的内存是绝对规整的&#xff0c;内存主要分为两部分&#xff0c;所有使用过的内存被放在一边&#x…...

Excel·VBA二维数组组合函数、组合求和

目录 1&#xff0c;二维数组组合函数举例 2&#xff0c;组合求和 之前的文章《ExcelVBA数组组合函数、组合求和》和《ExcelVBA数组排列函数》&#xff0c;都是针对一维数组的组合和排列 二维数组组合&#xff1a;对一个m行*n列的二维数组&#xff0c;每行抽取1个元素进行组合&a…...

调用自实现MyGetProcAddress获得CreateFileA函数并调用创建写入文件

写文件如下 #include <iostream> #include <Windows.h>typedef HANDLE(WINAPI* CreateFileAFunc)(LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);DWORD MyGetProcAddress(_In_ HMODULE hModule,_In_ LPCSTR lpProcName ){PIMAGE_DOS_HEADE…...

Leetcode 191.位1的个数

编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中数字位数为 1 的个数&#xff08;也被称为汉明重量&#xff09;。 提示&#xff1a; 请注意&#xff0c;在某些语言&#xff08;如 Java&#xff09;中…...

安防监控视频平台EasyCVR视频汇聚平台调用接口出现跨域现象的问题解决方案

视频监控汇聚EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;能对外分发RTSP、RTMP、FLV、HLS、WebRTC等格式的视…...

Python中的一些常用操作

文章目录 一. Python操作之-- 使用Python 提取PDF文件中的表格数据&#xff01;二&#xff1a;三&#xff1a; Python中的 staticmethodclassmethod方法四&#xff1a; 反斜杠 \五&#xff1a; 终端的解释器提示符号修改六&#xff1a; python使用json.dumps输出中文七&#xf…...

go语言调用python脚本

文章目录 代码gopython 在 go语言中调用 python 程序&#xff0c;你可能会用到 代码 亲测 go 测试 go 文件 func TestR(t *testing.T) {// 设置要执行的Python脚本和参数scriptPath : "../nansen.py"arg1 : "nansen"// 执行Python脚本cmd : exec.Comm…...

2.3 【MySQL】命令行和配置文件中启动选项的区别

在命令行上指定的绝大部分启动选项都可以放到配置文件中&#xff0c;但是有一些选项是专门为命令行设计的&#xff0c;比方说defaults-extra-file 、 defaults-file 这样的选项本身就是为了指定配置文件路径的&#xff0c;再放在配置文件中使用就没啥意义了。 如果同一个启动选…...

外部库/lib/maven依赖项 三者关系

外部库(存放项目初始配置的jar包)(它的文件夹里并没有包含lib文件夹的引的外部的依赖的jar包) lib(存放外部导入到项目的依赖的jar包) maven依赖项(管理项目所有的jar包依赖) 三者存放jar包的关系 项目所依赖的全部的jar包 maven依赖项的jar包 外部库中的jar包 lib中的…...

在线制作作息时间表

时光荏苒&#xff0c;岁月如梭&#xff0c;人们描述时光易逝的句子&#xff0c;多如星河。 一寸光阴一寸金&#xff0c;寸金难买寸光阴。 人生就是一段时间而已&#xff0c;所以我明白了一个道理 人生之中最大的浪费就是时间的浪费 因此我想我们教给我们孩子重要的一课应该也是…...

他们朝我扔泥巴(scratch)

前言 纯~~~属~~~虚~~~构~~~&#xff08;同学看完短视频要我做&#xff0c;蟹蟹你&#xff09; 用scratch做的&#xff0c;幼稚得嘞(&#xffe3;_&#xffe3;|||)呵呵&#xff08;强颜欢笑&#xff09; 完成视频 视频试了好久&#xff0c;就是传不上来&#xff0c;私信我加我…...

docker部署前端项目保姆级教程

本地启动docker&#xff08;有不会启动的吗&#xff1f;下载docker&#xff08;小海豚&#xff09;双击起来就行&#xff09; 准备阿里云账号&#xff08;免费&#xff09; 没有就去注册一个&#xff0c;记住密码后面要用到 官网地址&#xff1a;阿里云登录 - 欢迎登录阿里云…...

《C和指针》笔记13: static关键字总结

这里对static关键字做一下总结&#xff0c;可以回顾一下前面两篇博客的文章。 《C和指针》笔记11: external和internal链接属性 《C和指针》笔记12: 存储类型&#xff08;自动变量、静态变量和寄存器变量&#xff09; 当它用于函数定义时&#xff0c;或用于代码块之外的变量声…...

Docker harbor私有仓库部署与管理

一、搭建本地私有仓库二、Harbor私有仓库部署与管理1、Harbor概述2、Harbor的特性3、Harbor的核心组件3.1 Proxy3.2 Registry3.3 Core services3.3.1 UI&#xff08;harbor-ui&#xff09;3.3.2 WebHook3.3.3 Token 服务 3.4 Database&#xff08;harbor-db&#xff09;3.5 Log…...

解锁Selenium的力量:不仅仅是Web测试

Selenium简介 Selenium&#xff0c;作为Web应用测试的领军者&#xff0c;已经成为了无数开发者和测试人员的首选工具。它不仅仅是一个自动化测试工具&#xff0c;更是一个强大的Web应用交互框架。 Selenium的起源与发展 Selenium的历史可以追溯到2004年&#xff0c;由Jason Hu…...

[SQLITE_ERROR] SQL error or missing database (near “=“: syntax error)【已解决】

这个报的错误是语法错误&#xff0c;但是我并没有看出来这行代码有什么错。 通过排除掉下边两个问题解决的 从增加记录方法复制的下来的代码&#xff0c;只删除了关闭自动提交事务&#xff0c;但是connection.commit忘记删除executeQuery和executeUpdate方法的用法忘记了&…...

【视觉系统】笔芯内径机器视觉测量软硬件方案-康耐德智能

检测内容 笔芯内径机器视觉测量系统 检测要求 精度0.03mm&#xff0c;速度120~180个/分钟 视觉可行性分析 对样品进行了光学实验&#xff0c;并进行图像处理&#xff0c;原则上可以使用机器视觉系统进行测试测量。 结果&#xff1a; 对所有样品进行分析&#xff0c;可以在不…...

将文件夹的名称写到Excel中

查看文件夹名称 os.listdir()函数会返回指定路径下的所有文件和文件夹的名称列表&#xff0c;包括隐藏文件和文件夹 import osfolder_path . # 文件夹路径 # . is当前路径 files os.listdir(folder_path) # 获取文件夹内所有文件的名称列表for filename in files:print(fi…...

关于Vue CLI项目 运行发生了 less-lorder错误的解决方案

Module node found :Error: Can’t resolve ‘less-loader’ 报错 文章目录 Module node found :Error: Cant resolve less-loader 报错解决方案&#xff1a;安装 webpack 和 less安装 less-loader 问题&#xff1a; 在运行vue项目的时候发生&#xff1a; Module not found: Er…...

【Qt学习】02:信号和槽机制

信号和槽机制 OVERVIEW 信号和槽机制一、系统自带信号与槽二、自定义信号与槽1.基本使用student.cppteacher.cppwidget.cppmain.cpp 2.信号与槽重载student.cppteacher.cppwidget.cppmain.cpp 3.信号连接信号4.Lambda表达式5.信号与槽总结 信号槽机制是 Qt 框架引以为豪的机制之…...

软件工程(十三) 设计模式之结构型设计模式(一)

前面我们记录了创建型设计模式,知道了通过各种模式去创建和管理我们的对象。但是除了对象的创建,我们还有一些结构型的模式。 1、适配器模式(Adapter) 简要说明 将一个类的接口转换为用户希望得到的另一个接口。它使原本不相同的接口得以协同工作。 速记关键字 转换接…...

Node与Express后端架构:高性能的Web应用服务

在现代Web应用开发中&#xff0c;后端架构的性能和可扩展性至关重要。Node.js作为一个基于事件驱动、非阻塞I/O的平台&#xff0c;以及Express作为一个流行的Node.js框架&#xff0c;共同构建了高性能的Web应用服务。 在本文中&#xff0c;我们将深入探讨Node与Express后端架构…...

C++炸弹小游戏

游戏效果 小人可以随便在一些元素&#xff08;如石头&#xff0c;岩浆&#xff0c;水&#xff0c;宝石等&#xff09;上跳跃&#xff0c;“地面”一直在上升&#xff0c;小人上升到顶部或者没有血的时候游戏结束&#xff08;初始20点血&#xff09;&#xff0c;小人可以随意放炸…...

发送通知消息

目录 1 himall3.0商城源码 1.1 SendMessageOnOrderShipping 1.1.1 //发送通知消息 1.2 /// 所有订单是否都支付 1.2.1 //有待付款的订单&#xff0c;则未支付完成 himall3.0商城源码 public static List<InvoiceTitleInfo> GetInvoiceTitles(long userid) { re…...