C++ future/promise/thread/async/packaged_task入门

        std::promise、future、thread、async、packaged_task这些到底是个啥?

两种获取异步结果的方式

std::future

        std::future是一个同步原语,它代表了一个异步操作的结果。这个结果可能来自另一个线程、任务或者异步操作,而std::future提供了一种机制来访问这个结果。std::future通常与std::async、std::promise或std::packaged_task一起使用。
        std::future对象封装了一个值,这个值在将来某个时刻才会变得可用。当异步操作完成时,它的结果(或异常)会存储在std::future对象中。可以通过std::future对象来检索这个结果,或者等待异步操作完成。

获取std::future
可以通过以下几种方式获取std::future对象:

  • 通过std::async启动一个异步任务时,它会返回一个std::future对象。
  • 通过调用std::promise对象的get_future方法。
  • 通过std::packaged_task对象的get_future方法。

使用std::future获取结果

  • get()方法用来获取存储在其中的值。如果异步操作还没有完成,get方法会阻塞当前线程,直到值变得可用。调用完get,feature对象不再持有该结果。
  • wait()方法用来等待异步操作完成,而不获取结果

异常处理
        如果异步操作抛出了异常,这个异常会被捕获并存储在std::future对象中。当你调用get方法时,异常会被重新抛出。

try {int x = fut.get();
} catch (std::exception& e) {// 处理异常
}

std::future对象在默认构造时不关联任何异步操作。只有当它与一个特定的异步操作关联后,它才变得有用。std::future对象是不可复制的,但它们可以被移动。这意味着你可以将它们从一个函数传递到另一个函数,但不能创建它们的副本。

        通过std::future,开发者可以轻松地管理异步操作的结果,无论是来自std::async启动的任务,还是std::promise和std::packaged_task的结果。std::future提供了一种安全且简单的方式来同步线程间的操作,使得编写并发和异步代码变得更加容易和安全。

std::promise

        std::promise是一个同步原语,它提供了一种在一个线程中存储一个值或异常,并在另一个线程中通过std::future对象来检索它的机制。std::promise和std::future通常一起使用,以在线程间传递数据或通知。

创建std::promise

#include <chrono>
#include <thread>
#include <future>using namespace std;void SlowAdd(int a, int b, promise<int>&& p) {this_thread::sleep_for(std::chrono::seconds(1));p.set_value(a + b);
}int main() {promise<int> p;// 与每个std::promise对象关联的是一个std::future对象future<int> f = p.get_future();thread t(SlowAdd, 1, 2, move(p));cout << f.get() << endl;t.join();return 0;
}

输出:

code % ./a.out       
3  // after 1 sec

异常处理
如果在计算过程中发生异常,你可以使用std::promise的set_exception方法来存储异常:

try {// 执行一些可能抛出异常的计算
} catch (...) {myPromise.set_exception(std::current_exception());
}

        在另一个线程中,当你尝试通过std::future对象获取结果时,如果有异常被存储,它将被重新抛出。std::promise对象一旦通过set_value或set_exception设置了值或异常,就不能再次设置,否则将会抛出std::future_error异常。

        此外,std::promise对象在析构时会自动释放与之关联的资源,但如果你在析构前没有设置值或异常,与之关联的std::future对象在调用get()时将抛出std::future_error异常。

两种异步执行任务的方式

std::thread

        std::thread代表了一个单独的执行线程。创建一个std::thread对象时,你可以传递一个函数或者任何可调用对象(包括lambda表达式、函数指针、成员函数指针和具有operator()的对象)给它,这个函数或对象将在新线程中执行。

创建线程

#include <iostream>
#include <chrono>
#include <thread>using namespace std;void Add(int a, int b) {cout << "sum: " << a + b << endl;
}class C_Sub {
public:void Sub(int a, int b) {cout << "sub: " << a - b << endl;}
};int main() {thread t1(Add, 1, 2);t1.join();this_thread::sleep_for(std::chrono::seconds(1));C_Sub sub;thread t2(&C_Sub::Sub, sub, 3, 2);t2.join();return 0;
}

输出:

code % ./a.out       
sum: 3
sub: 1

线程管理
        创建线程后,就需要管理它的生命周期。通常,你需要在某个时刻等待线程结束,这可以通过调用join()来实现。如果你没有在std::thread对象销毁前调用join()或者detach(),或者重复调用了join()或detach(),那么在其析构函数中会调用std::terminate(),程序将异常终止。

  • join():等待线程结束。
  • detach():允许线程独立运行,即使创建它的std::thread对象已经被销毁。
  • joinable(): 判断一个thread是否是可join或detach的,即没有调用过join或detach

线程与异常
        如果线程函数抛出了一个未捕获的异常,std::terminate()将被调用,程序将终止。因此,你应该确保线程函数中的异常被妥善处理。

std::async

        std::async是一个函数模板,它用于异步执行一个任务,并返回一个std::future对象。这个std::future对象可以用来获取异步任务的结果。std::async可以选择立即启动一个新线程来执行任务,也可以延迟执行任务直到对应的std::future对象的get()或wait()被调用。

使用std::async启动异步任务

#include <iostream>
#include <chrono>
#include <future>using namespace std;int SlowAdd(int a, int b) {this_thread::sleep_for(std::chrono::seconds(1));return a + b;
}int main() {future<int> f = async(SlowAdd, 1, 2);cout << f.get() << endl;return 0;
}

输出:

code % ./a.out       
3  // after 1 sec

std::async的启动策略
        std::async可以接受一个可选的启动策略参数,这个参数控制异步任务的执行方式。

  • std::launch::async:指示std::async创建一个新线程来立即执行任务。
  • std::launch::deferred:指示std::async延迟执行任务,直到调用std::future对象的get()或wait()。

        如果不指定启动策略,std::async可能会根据实现选择最合适的策略。

异步任务的异常处理
        如果异步任务抛出了异常,这个异常不会立即传播到启动任务的线程。相反,异常会被存储在std::future对象中,当你调用get()来获取结果时,异常会被重新抛出。

std::async与std::thread的比较
        std::async与std::thread都可以用来执行并发任务,区别:

  • std::async返回一个std::future对象,可以用来获取任务的结果或异常,而std::thread不直接提供这样的机制。
  • std::async管理任务的生命周期,当std::future对象被销毁时,它会等待任务完成。而std::thread需要显式地调用join()或detach()。
  • std::async可以延迟执行任务,而std::thread总是立即启动一个新线程。

一种绑定feature与任务的方式

std::packaged_task

        std::packaged_task是C++11引入的一个模板类,它封装了一个可调用对象(如函数、lambda表达式、绑定表达式或其他函数对象),并允许其异步执行。它的一个关键特性是能够提供一个std::future对象,通过这个对象可以在未来某个时刻获取std::packaged_task所封装的可调用对象的返回值。

如何使用std::packaged_task

#include <iostream>
#include <chrono>
#include <future>using namespace std;int SlowAdd(int a, int b) {this_thread::sleep_for(std::chrono::seconds(1));return a + b;
}int main() {std::packaged_task<int(int)> task(SlowAdd);std::future<int> f = task.get_future();std::thread t(std::move(task), 1, 2);std::cout << f.get() << std::endl;t.join();return 0;
}

输出:

code % ./a.out       
3  // after 1 sec

异常处理
        如果std::packaged_task中的可调用对象在执行过程中抛出异常,这个异常将被捕获,并存储在与之关联的std::future对象中。当你调用std::future::get()时,如果有异常发生,它将被重新抛出。

重置任务
        std::packaged_task提供了一个reset成员函数,允许你重置任务的状态,这样你就可以重新使用std::packaged_task对象。但是,在重置之前,你必须确保已经获取了之前任务的结果,否则会抛出std::future_error异常。

有async为什么还要有packaged_task

        std::async是一个函数模板,它用于简化异步任务的创建和执行。当你调用std::async时,它会自动创建一个线程(或者将任务提交给线程池,这取决于实现和传递的策略参数),并立即开始执行指定的任务。std::async返回一个std::future对象,你可以通过它来获取任务的结果。

        std::async的优点在于它的简单性和便捷性。你不需要显式创建线程或管理线程的生命周期,一切都由std::async自动处理。

        std::packaged_task是一个类模板,它将一个可调用对象和一个std::future对象绑定在一起。与std::async不同,std::packaged_task不会自动启动任务执行,你需要手动调用它或将其传递给一个线程来执行。这意味着你可以更精细地控制任务的执行时机和方式。

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

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

相关文章

【LeetCode:200. 岛屿数量 | DFS 】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

使用微信读书高效阅读论文,自带翻译功能。

下面以“向文本到图像扩散模型添加条件控制”&#xff08;Adding Conditional Control to Text-to-Image Diffusion Models&#xff09;这篇论文示例下阅读效果。 论文地址&#xff1a;https://arxiv.org/abs/2302.05543 选择右侧的download PDF, 然后进入论文预览页面&#x…

记录误删除docker中极狐gitlab容器恢复过程

如题一次误操作导致删除了docker中极狐gitlab容器恢复过程 情况说明 创建容器时&#xff0c;我是用的是极狐官网推荐安装的步骤&#xff0c;具体按照官网步骤走就行 sudo docker run --detach \--hostname gitlab.example.com \--publish 443:443 --publish 80:80 --publish …

有效的回文

常用方法就是双指针。使用两个指针从字符串的两端向中间移动&#xff0c;同时比较对应位置的字符&#xff0c;直到两个指针相遇。由于题目忽略非字母和非数字的字符且忽略大小写&#xff0c;所以跳过那些字符&#xff0c;并将字母转换为小写&#xff08;或大写&#xff09;进行…

SpringBoot默认配置文件

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: 循序渐进学SpringBoot ✨特色专栏: MySQL学习 🥭本文内容:SpringBoot默认配置文件 📚个人知识库: Leo知识库,欢迎大家访问 1.前言☕…

公网环境使用移动端设备+cpolar远程访问本地群晖nas上的影视资源

文章目录 1.使用环境要求&#xff1a;2.下载群晖videostation&#xff1a;3.公网访问本地群晖videostation中的电影&#xff1a;4.公网条件下使用电脑浏览器访问本地群晖video station5.公网条件下使用移动端&#xff08;搭载安卓&#xff0c;ios&#xff0c;ipados等系统的设备…

杨中科 .NETCORE 异步编程

一、 为什么需要异步编程 异步点餐的优点&#xff1a;能同时服务多个客人 异步点餐一定会提升单个客户点餐速度吗&#xff1f; 答案理所当然&#xff1a;不能 图片美化服务例子服务器能够同时服务的请求数量有限 void BeautifyPic (File photo, Response response) {byte[] …

企业出海合规:如何区分数据控制者与数据处理者

什么是数据控制者&#xff1f; 数据控制者确定个人数据的处理目的和方式。若该企业有权决定处理个人数据的“原因”和“方式”&#xff0c;那么它就是数据控制者。在GDPR中&#xff0c;数据控制者在保护数据主体&#xff08;例如网站用户&#xff09;的隐私和权利方面负有最大…

【排序】归并排序(C语言实现)

文章目录 1. 递归版的归并排序1.1 归并排序的思想2. 递归版的归并排序的实现 2. 非递归版的归并排序 1. 递归版的归并排序 1.1 归并排序的思想 归并排序&#xff08;MERGE - SORT&#xff09;是建立在归并操作上的一种有效的排序算法, 该算法是采用分治法&#xff08;Divide a…

python进行简单的app自动化测试(pywinauto)+ 截屏微信二维码

一、开始需要了解准备 1、安装 pip install pywinauto2、选择&#xff08;后面会通过工具进行判断用哪个&#xff09; 3、自动化控制进程的范围 示例 Application单进程 Desktop多进程 4、程序辅助检测工具 3中的下载连接 链接 点击放大镜拖到对应位置即可 二、简单的开始…

函数指针和回调函数

文章目录 一.函数指针1.什么是函数指针2.函数指针的形式3.函数指针的用途。1.调用函数2.作为参数进行传递 二.函数指针数组三.回调函数 一.函数指针 1.什么是函数指针 函数指针是指向函数的指针。在C语言和C中&#xff0c;函数指针可以用来存储函数的地址&#xff0c;并且可以…

RT-Thread:STM32实时时钟 RTC开启及应用

说明&#xff1a;STM32F103/407系列基于 RT-Thread 系统的 RTC 开启及应用 应用流程介绍。 1. RTC功能开启 1.1 开启系统RTC驱动 1.2 打开系统RTC相关的宏 1.3 打开库函数 RTC 相关的宏 完成以上系统配置&#xff0c;编译无误情况下RTC 就已经开启了。 2. RTC 应用 官方 AP…