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

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.lryc.cn/news/623276.html

相关文章:

  • MATLAB基础训练实验
  • 超越“调参”:从系统架构师视角,重构 AI 智能体的设计范式
  • 深度剖析Redisson分布式锁项目实战
  • 【数据分享】大清河(大庆河)流域上游土地利用
  • AutoDL使用学习
  • K8s核心组件全解析
  • 服务器配置开机自启动服务
  • GEEPython-demo1:利用Sentinel-2监测北京奥林匹克森林公园2024年NDVI变化(附Python版)
  • [CSP-J2020] 方格取数
  • Vue组件生命周期钩子:深入理解组件的生命周期阶段
  • Vue 3.5+ Teleport defer 属性详解:解决组件渲染顺序问题的终极方案
  • 【P14 3-6 】OpenCV Python——视频加载、摄像头调用、视频基本信息获取(宽、高、帧率、总帧数)
  • ESP32-S3_ES8311音频输出使用
  • CSS 核心知识点全解析:从基础到实战应用
  • 探索粒子世界:从基础理论到前沿应用与未来展望
  • 主从复制+哨兵
  • 【论文阅读】Multimodal Graph Contrastive Learning for Multimedia-based Recommendation
  • List容器:特性与操作使用指南
  • 《设计模式》代理模式
  • Java 9 新特性及具体应用
  • 什么是微前端?
  • XC6SLX45T-2FGG484C Xilinx AMD Spartan-6 FPGA
  • 两个简单的设计模式的例子
  • [Linux] Linux文件系统基本管理
  • 没学过音乐怎么写歌?豆包 + 蘑兔
  • Python Condition对象wait方法使用与修复
  • 《设计模式》装饰模式
  • Tello无人机与LLM模型控制 ROS
  • 二十六、Mybatis-XML映射文件
  • 行为型设计模式:对象协作的舞蹈家(中)