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

CPP多线程3:async和future、promise

大多数情况下使用async而不用thread。thread可以快速、方便地创建线程,但在async面前,就是小巫见大巫了。async可以根据情况选择同步执行或创建新线程来异步执行,当然也可以手动选择。对于async的返回值操作也比thread更加方便。

std::async

暂且不管它的返回值std::future是啥(后面解释),先举个例再说。

#include <iostream>
#include <future>
#include <string>
using namespace std;int main(){async(launch::async,[](const string& msg){cout<<msg;},"Hello ");cout<<"World!"<<endl;return 0;
}

你的编译器可能会给出一条警告,

warning C4834: 放弃具有 "nodiscard" 属性的函数的返回值。

这是因为编译器不想让你丢弃async的返回值std::future,不过在这个例子中不需要它,忽略这个警告就行了。

std::async参数

不同于thread,async是一个函数,所以没有成员函数。

在这里插入图片描述
std::launch强枚举类(enum class)

std::launch有2个枚举值和1个特殊值:

在这里插入图片描述

std::future

我们已经知道如何使用async来异步或同步执行任务,但如何获得函数的返回值呢?这时候,async的返回值std::future就派上用场了。例子如下:

#include <iostream>
#include <future>
using namespace std;// 可变参数模板求和函数
template <class... Args>
auto sum(Args &&...args){return (0 + ... + args);  // 折叠表达式(C++17)
}int main()
{// 用lambda包装sum调用,显式传递参数,避免类型推导问题auto fut = async(launch::async, []{ return sum(1, 2, 33);  // 在lambda内部内部直接调用sum,参数明确});cout << fut.get() << endl; return 0;
}

我们定义了一个函数sum,它可以计算多个数字的和,之后我们又定义了一个对象fut,它的类型是std::future,这里的int代表这个函数的返回值是int类型。在创建线程后,我们使用了future::get()来阻塞等待线程结束并获取其返回值。至于sum函数中的折叠表达式(fold expression),不是我们这篇文章的重点。

future常用成员函数

构造&析构函数
在这里插入图片描述
常用成员函数
在这里插入图片描述
为啥要有void特化的std::future?

std::future的作用并不只有获取返回值,它还可以检测线程是否已结束、阻塞等待,所以对于返回值是void的线程来说,future也同样重要。

#include <iostream>
#include <future>
using namespace std;int main(){auto fut=async(launch::async,[](){for(int i=0;i<20'0000'0000;i++);});cout<<"Please wait ";while(fut.wait_for(chrono::milliseconds(100))!=future_status::ready)cout<<".";// fut.get();// 上面也会阻塞代码,且无任何返回值cout<<endl<<"Finished!"<<endl;return 0;
}

std::promise

在上文,我们已经讲到如何获取async创建线程的返回值。不过在某些特殊情况下,我们可能需要使用thread而不是async,那么如何获得thread的返回值呢?

还记得之前我们讲的thread成员函数吗?thread::join()的返回值是void类型,所以你不能通过join来获得线程返回值。那么thread里有什么函数能获得返回值呢?

答案是:没有

惊不惊喜?意不意外?thread竟然不能获取返回值!难道thread真的就没有办法返回点什么东西吗?如果你真是那么想的,那你就太低估C++了。一些聪明的人可能已经想到解决办法了:可以通过传递引用的方式来获取返回值。

但是单纯地使用&无法解决多线程竞争的问题,于是std提供了promise。promise实际上是std::future的一个包装,在讲解future时,我们并没有牵扯到改变future值的问题,但是如果使用thread以引用传递返回值的话,就必须要改变future的值,那么该怎么办呢?

实际上,future的值不能被改变,但你可以通过promise来创建一个拥有特定值的future。什么?没听懂?好吧,那我就举个例子:

constexpr int a = 1;

现在,把常量当成future,把a当作一个future对象,那我们想拥有一个值为2的future对象该怎么办?很简单:

constexpr int a = 1;
constexpr int b = 2;

这样,我们就不用思考如何改动a的值,直接创建一个新常量就能解决问题了。

promise的原理就是这样,不改变已有future的值,而是创建新的future对象。什么?还没听懂?好吧,记住这句话:

future的值不能改变,promise的值可以改变。

其使用示例如下:

#include <iostream>
#include <thread>
#include <future>
using namespace std;int main()
{promise<int> p;thread t([](promise<int> &p){int temp=0;for(int i=0;i<10;i++){temp+=i;}p.set_value(temp);}, ref(p));t.join();cout << p.get_future().get() << endl;return 0;
}

promise常用成员函数

构造&析构函数
在这里插入图片描述
常用成员函数
在这里插入图片描述

http://www.xdnf.cn/news/18020.html

相关文章:

  • ArrayList的contains问题
  • 机器学习 [白板推导](十二)[卡曼滤波、粒子滤波]
  • 第G7周:Semi-Supervised GAN 理论与实战
  • 【科研绘图系列】R语言绘制雷达图
  • 洛谷B3865 [GESP202309 二级] 小杨的 X 字矩阵(举一反三)
  • 从 MySQL 5.7 迁移到 8.0:别让 SQL 文件 “坑” 了你
  • 《从入门到高可用:2025最新MySQL 8.0全栈速通指南》
  • Linux配置Dante使用的pam验证
  • 【攻防实战】红队攻防之Goby反杀
  • 力扣(LeetCode) ——622. 设计循环队列(C语言)
  • Android Jetpack | Lifecycle
  • 6JSON格式转python并实现数据可视化
  • 储能领域大数据平台的设计中如何使用 Hadoop、Spark、Flink 等组件实现数据采集、清洗、存储及实时 / 离线计算,支持储能系统分析与预测
  • 人工智能中的(特征选择)数据过滤方法和包裹方法
  • 2-3〔O҉S҉C҉P҉ ◈ 研记〕❘ 漏洞扫描▸AppScan(WEB扫描)
  • KingbaseES主备读写分离集群安装教程
  • 计算机网络:(十五)TCP拥塞控制与拥塞控制算法深度剖析
  • C++自旋锁的后退机制简介
  • 云原生俱乐部-RH124知识点总结(3)
  • 基于springboot的在线视频教育管理系统设计与实现(源码+文档+部署讲解)
  • 一文了解金融合规
  • 数据结构初阶(17)排序算法——非比较排序(计数排序·动图演示)、排序算法总结
  • Java内功修炼(1)——时光机中的并发革命:从单任务到Java多线程
  • 【论文阅读笔记】--Eurosys--HCache
  • ROS相关的ubuntu基础教程
  • vue3动态的控制表格列的展示简单例子
  • 基于FPGA的实时图像处理系统(1)——SDRAM回环测试
  • XC6SLX45T-2FGG484C Xilinx AMD Spartan-6 FPGA
  • 利用爬虫按图搜索淘宝商品(拍立淘)实战指南
  • vue:vue3 watch 属性