当前位置: 首页 > 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.dtcms.com/a/334335.html

相关文章:

  • 【八股】计网-计算机网络-秋招
  • 让数据库交互更优雅:MyBatis核心机制深度解析(附实战视频教程)
  • 【DL学习笔记】常用数据集总结
  • 详解flink java基础(二)
  • 使用nvm查看/安装node版本
  • Spring AI 进阶之路01:三步将 AI 整合进 Spring Boot
  • 【科研绘图系列】R语言绘制雷达图
  • MySQL 配置性能优化赛技术指南
  • MySQL 配置性能优化赛技术文章
  • 从 MySQL 5.7 迁移到 8.0:别让 SQL 文件 “坑” 了你
  • 【笔记】动手学Ollma 第一章 Ollama介绍
  • 玉米及淀粉深加工产业展|2026中国(济南)国际玉米及淀粉深加工产业展览会
  • 数据清洗处理
  • 系统思考—啤酒游戏经营决策沙盘认证
  • 如何用 BCG 矩阵与生命周期模型联合做产品组合管理
  • GTSAM中iSAM2 实时优化后做全局 LM/GN 优化时如何检测并剔除错误(outlier)因子约束详解和工程应用
  • MySQL深度理解-Innodb底层原理
  • 设计模式之【快速通道模式】,享受VIP的待遇
  • Java基础 8.16
  • 【OpenGL】LearnOpenGL学习笔记09 - 材质、光照贴图
  • React手撕组件和Hooks总结
  • ★CentOS:MySQL数据备份
  • 学习安卓APP开发,10年磨一剑,b4a/Android Studio
  • CPP多线程2:多线程竞争与死锁问题
  • 企业级Java项目金融应用领域——银行系统
  • C#WPF实战出真汁09--【消费开单】--选择菜品
  • 驱动开发系列63 - 配置 nvidia 的 open-gpu-kernel-modules 调试环境
  • AI重构文化基因:从“工具革命”到“生态觉醒”的裂变之路
  • 【101页PPT】芯片半导体企业数字化项目方案汇报(附下载方式)
  • 在鸿蒙应用中快速接入地图功能:从配置到实战案例全解析