一、前言
在本篇博文中,博主将为大家带来【C++从零实现Json-Rpc框架】的第三弹,即C++11中的异步操作,希望大家能够从中有所收获
二、正文
1.std::future 介绍
std::future是C++11标准库中的⼀个模板类,它表⽰⼀个异步操作的结果。当我们在多线程编程中使⽤异步任务时,std::future可以帮助我们在需要的时候获取任务的执⾏结果。std::future的⼀个重要特性是能够阻塞当前线程,直到异步操作完成,从⽽确保我们在获取结果时不会遇到未完成的操作。
注:std::futrue 本质上不是一个异步任务,而是一个辅助我们获取异步任务结果的东西
2. 应用场景
• 异步任务:当我们需要在后台执⾏⼀些耗时操作时,如⽹络请求或计算密集型任务等,std::future 可以⽤来表⽰这些异步任务的结果。通过将任务与主线程分离,我们可以实现任务的并⾏处理,从而提⾼程序的执⾏效率
• 并发控制:在多线程编程中,我们可能需要等待某些任务完成后才能继续执⾏其他操作。通过使⽤ std::future,我们可以实现线程之间的同步,确保任务完成后再获取结果并继续执⾏后续操作
• 结果获取:std::future提供了⼀种安全的⽅式来获取异步任务的结果。我们可以使⽤ std::future::get()函数来获取任务的结果,此函数会阻塞当前线程,直到异步操作完成。这样,在调⽤get()函数时,我们可以确保已经获取到了所需的结果
3. 用法介绍与示例
std::future并不能单独使用,需要搭配一些能够执行异步任务的模板类或者函数一起使用
异步任务搭配使用:
●std::async函数模版:异步执行一个函数,返回一个future对象用于获取函数结果
●std::packaged_task类模版:为一个函数生成一个异步任务对象(可调用对象),用于在其他线程中执行
●std::promi类模板:示例的对象可以返回一个对象,在其他线程中向promise对象设置数据,其他线程的关联future就可以获取数据
● 使用std::async关联异步任务
std::async是⼀种将任务与std::future关联的简单⽅法。它创建并运⾏⼀个异步任务,并返回⼀个与该 任务结果关联的std::future对象。默认情况下,std::async是否启动⼀个新线程,或者在等待future 时,任务是否同步运⾏都取决于你给的参数。这个参数为std::launch类型:
★ std::launch::deferred 表明该函数会被延迟调⽤,直到在future上调⽤get()或者wait()才会开始 执⾏任务
★ std::launch::async 表明函数会在⾃⼰创建的线程上运⾏
注: std::launch::deferred | std::launch::async内部通过系统等条件⾃动选择策略,也就是在调用async函数的时候可以通过参数的传递来选择任务执行的方式是同步还是异步。
#include <iostream>
#include <future>
#include <bits/this_thread_sleep.h>
int Add(int num1, int num2){
std::cout<<num1+num2<<std::endl;
return num1+num2;
}
int main()
{
//std::lauch::async策略:内部创建一个线程执行函数,函数运行结果通过future获取
//std::lauch::deferred策略::同步策略,获取结果的时候再去执行任务
std::future<int> res=std::async(std::launch::async,Add,11,22);//进行了一个异步非阻塞调用
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout<<"------------------------\\n";
//std::future<int>::get() 用于获取异步任务的结果,如果还没有结果就会阻塞
std::cout<<res.get()<<std::endl;
return 0;
}
为了证明Add任务的执行确实是异步执行的我们将主线程休眠了1s,如果是异步的,那么Add会先打印出结果 ,再由主线程打印出分隔符
● 使⽤std::packaged_task和std::future配合
std::packaged_task就是将任务和std::feature绑定在⼀起的模板,是⼀种对任务的封装。我们可以通 过std::packaged_task对象获取任务相关联的std::feature对象,通过调⽤get_future()⽅法获得。 std::packaged_task的模板参数是函数签名。可以把std::future和std::async看成是分开的,⽽std::packaged_task则是⼀个整体
#include <iostream>
#include <future>
#include <thread>
#include <memory>
int Add(int num1,int num2)
{
std::cout<<"into add!\\n";
return num1+num2;
}
int main()
{
//1. 封装任务
auto task = std::make_shared<std::packaged_task<int(int,int)>>(Add);
//2. 获取任务包关联的future对象
std::future<int> res =task->get_future();
std::thread thr([task](){
(*task)(11,22);
});
//3. 获取结果
std::cout<<res.get()<<std::endl;
thr.join();
return 0;
}
● 使⽤std::promise和std::future配合
std::promise提供了⼀种设置值的⽅式,它可以在设置之后通过相关联的std::future对象进⾏读取。换 种说法就是之前说过std::future可以读取⼀个异步函数的返回值了,但是要等待就绪,⽽std::promise 就提供⼀种⽅式⼿动让std::future就绪
#include <iostream>
#include <future>
#include <thread>
#include <memory>
int Add(int num1,int num2)
{
std::cout<<"into add!\\n";
return num1+num2;
}
int main()
{
//1. 在使用的时候,就是先实例化一个指定结果的promis对象,
std::promise<int> pro;
//2. 通过promise对象,获取关联的future对象
std::future<int> res=pro.get_future();
//3. 在任意位置给promise设置数据,就可以通过关联的furture获取到这个设置的数据了
std::thread thr([&pro](){
int sum=Add(11,22);
pro.set_value(sum);
});
std::cout<<res.get()<<std::endl;
thr.join();
return 0;
}
● 总结
①std::async是一个模版函数,内部会创建线程执行异步任务
②std::packaged_task是一个模板类,是一个任务包,是对一个函数进行二次封装,封装成一个可调用对象作为任务放到其他线程执行的。任务包封装好了可以在任意位置进行调用,通过关联的future来获取执行结果
执行任务样例:
1.封装任务
2.执行任务
3.通过futr获取任务执行结果
③std::promise是一个模板类,是对与结果的封装
1.在使用的时候,就是先实例化一个指定结果的promise对象
2.通过promise对象,获取关联的future对象
3.在任意位置给promise设置数据,就可以通过关联的future获得到这个设置的数据了
三、结语
到此为止,本文关于从零实现Json-RPC框架第三弹的内容到此结束了,如有不足之处,欢迎小伙伴们指出呀!
关注我 _麦麦_分享更多干货:_麦麦_-CSDN博客
大家的「关注❤️ + 点赞👍 + 收藏⭐」就是我创作的最大动力!谢谢大家的支持,我们下期见!!
平台声明:以上文章转载于《CSDN》,文章全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,仅作参考。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/m0_73953114/article/details/147199921