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

期值(future)

链接异步操作和异步操作的管道

创建一个异步调用,然后其他线程执行这个异步调用,异步调用执行完成之后会返回一个结果,然后异步调用创建方在需要使用这个异步调用结果的时候,通过某种方式来获取这个异步调用结果。C++针对上述需求,提供了一些类对象来完成上面的过程,本文中讨论的C++提供的两种对象为std::futurestd::promise,二者的关系对应如下

如上图所示,异步调用创建的时候,会返回一个std::future对象实例给异步调用创建方。异步调用执行方持有std::promise对象实例。双方持有的std::promise对象实例和std::future对象实例分别连接一个共享对象,这个共享对象在异步调用创建方和异步调用执行方之间构建了一个信息同步的通道(channel),双方通过这个通道进行异步调用执行情况的信息交互

异步调用执行方访问这个通道是通过自身持有的std::promise实例来向通道中写入值的,异步调用创建方是通过自身持有的std::future对象实例来获取通道中的值的。当异步调用执行方完成异步调用的执行之后,会通过std::promise对象实例向通道中写入异步调用执行的结果值。异步调用创建方通过自身持有的std::future对象实例来获取异步调用结果

异步调用执行方通过std::promise来兑现承诺(promise,承诺调用完成之后在未来某一时刻交付结果),异步调用创建方通过std::future来获取这个未来的值(future,未来兑现的承诺对应的结果值)

示例

下面,我们通过一段程序示例,来看一下上面这个模型对应的程序,程序如下

上述代码行4~6,创建了一个promise对象,并且从该对象实例中获取到了用于获取承诺兑现的值的std::future对象实例,这样就构建了一个异步调用创建方(发起方)和异步调用执行方之间用于传递异步调用结果的数据通道

在异步调用任务中,完成计算之后,通过std::promise::set_value来进行承诺兑现,将异步调用的结果写入通道中(对应代码行13)。异步调用创建方通过std::future中的get方法来获取异步调用的结果(对应代码行18

上面代码对应的终端输出如下

 async result is 21

有一个细节值得注意,上面代码行12中在异步调用任务执行的时候,可以通过sleep_for将当前线程(异步任务执行的线程)休眠了500ms。这里是为了让异步调用的结果在异步调用创建线程获取结果的代码调用(对应代码行18)之后在进行承诺兑现(通过set_value交付异步调用的结果)。也就是说程序执行到代码行18的时候,异步调用的结果还没有设置到通道中。但是最终终端还是正常的打印了结果。这是因为当调用std::future::get方法获取异步调用结果的时候,如果此时异步调用没有执行完成,即没有向通道内写入异步调用的结果(异步调用结果没有ready),此时当前线程会阻塞直到异步调用结果计算完成并且设置

88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

期值(future)

C++11 提供了:

• future——一个句柄,通过它你可以从一个共享的单对象缓冲区中 get() 一个值,可能需要等待某个 promise 将该值放入缓冲区。

• promise——一个句柄,通过它你可以将一个值 put() 到一个共享的单对象缓冲区,可能会唤醒某个等待 future 的 thread。

• packaged_task——一个类,它使得设置一个函数在线程上异步执行变得容易,由 future 来接受 promise 返回的结果。

• async()——一个函数,可以启动一个任务并在另一个 thread 上执行。

11111111111111111111111111111111111111111111

c++基础语法之future,promise,async详细讲解-千里马SurfaceFlinger学习必备

2024年02月06日 11:22344浏览 · 0点赞 · 0评论

千里马学框架

粉丝:3142文章:109

关注

背景

hi,粉丝朋友们:在开始学习SurfaceFlinger相关源码分析时候,发现也有大量的新语法,这里如果需要顺利看懂代码就不得不学习一些新版本c++基础语法。比如看到如下的drawLayers这个方法时候,当时trace中看到是有线程切换,这里代码我们看到是future和promise。

图片

在这里插入图片描述

图片

在这里插入图片描述

trace可以看出来有线程切换和等待

图片

在这里插入图片描述

为啥新版本要出现std::future和std::promise呢? 
以前版本创建了线程以后,我们不能直接从thread.join()得到结果,必须定义一个变量,在线程执行时,对这个变量赋值,然后其他线程执行join()等待线程执行完成,再获取这个值,过程相对繁琐。   thread库提供了future用来访问异步操作的结果。std::promise用来包装一个值将数据和future绑定起来,为获取线程函数中的某个值提供便利,取值是间接通过promise内部提供的future来获取的,也就是说promise的层次比future高。

future

官方解释参考地址: https://legacy.cplusplus.com/reference/future/future/?kw=future 

获取线程返回值的类std::future,用来访问异步操作的结果。因为异步操作需要时间,future并不能马上获取其结果,只能在未来某个时候获取期待值,因此被称为future。 可以通过同步等待的方式,通过查询future的状态(future_status),来获取异步操作的结果。

future_status状态有3种: 

1)future_status::deferred 异步操作还没开始;

2)future_status::ready  异步操作已经完成; 

3)future_status::timeout  异步操作超时;

获取future异步操作的结果,有4种方式: 

1)get 等待异步结束并返回结果;

2)wait 只是等待异步操作结束,没有返回值; 

3)wait_for 超时等待异步操作返回结果,注意这里的返回结果就是future_status 4)wait_until 等待达到指定的时间点,异步操作返回结果,常配合当前时间点 + 时间段来设置目标时间点;

平常开发中使用get最多,因为一般使用future是想要获取另一个线程的执行结果,一般不会单独使用这个future类,会结合async和promise一起使用

async

std::async,线程异步操作函数std::async,可用来创建异步task,其返回结果保存到future对象中。当需要取得异步结果时,调用future.get()即可。如果不关注结果,只是简单等任务完成,可以使用future.wait() 方法原型,查询地址https://legacy.cplusplus.com/:

unspecified policy (1) 

template <class Fn, class... Args>
  future<typename result_of<Fn(Args...)>::type>
    async (Fn&& fn, Args&&... args);

specific policy (2) 

template <class Fn, class... Args>
  future<typename result_of<Fn(Args...)>::type>
    async (launch policy, Fn&& fn, Args&&... args);

参数解释如上图所示 第一个核心参数的launch参数,代表线程创建的策略 1)std::launch::async 调用async时就开始创建线程; 2)std::launch::deferred 延迟加载方式创建线程。调用async时不创建线程,直到调用了future的get或者wait,才创建线程。
默认情况都是情况1 第二个是线程执行方法函数 第三个代表执行线程函数时候需要传递的参数

案例如下:

// async example
#include <iostream>       // std::cout
#include <future>         // std::async, std::future
#include <sys/types.h>
#include <unistd.h>
using namespace std;
// a non-optimized way of checking for prime numbers:
bool is_prime (int x) {
  std::cout << "Calculating. Please, wait... thread Id = "<< gettid()<<endl;
  for (int i=2; i<x; ++i) if (x%i==0) return false;
  return true;
}
int main ()
{
  // call is_prime(313222313) asynchronously:
  std::future<bool> fut = std::async (is_prime,313222313);

  std::cout << "Checking whether 313222313 is prime main thread Id = "<< gettid()<<endl;
  // ...

  bool ret = fut.get();      // waits for is_prime to return

  if (ret) std::cout << "It is prime!\n";
  else std::cout << "It is not prime.\n";

  return 0;
}

执行结果如下:

图片

在这里插入图片描述

明显看到这里的调用async后就有两个线程了,主线程也是要等到子线程执行完成后才可以进行执行

promise

Promise
A promise is an object that can store a value of type T to be retrieved by a future object (possibly in another thread), offering a synchronization point.

On construction, promise objects are associated to a new shared state on which they can store either a value of type T or an exception derived from std::exception.

This shared state can be associated to a future object by calling member get_future. After the call, both objects share the same shared state:
- The promise object is the asynchronous provider and is expected to set a value for the shared state at some point.
- The future object is an asynchronous return object that can retrieve the value of the shared state, waiting for it to be ready, if necessary.

The lifetime of the shared state lasts at least until the last object with which it is associated releases it or is destroyed. Therefore it can survive the promise object that obtained it in the first place if associated also to a future.

协助线程赋值的类std::promise,将数据和future绑定。在线程函数中,为外面传入的promise对象赋值,线程函数执行完毕后,就可以通过promise的future获取该值。

应用场景

小疑问:为啥有了future这个后还需要promise呢?这个问题回答其实可以考虑一下future的获取,future都是需要几个固定的异步线程调用方式,比如async这种,但是很多时候不喜欢用这种异步方式,那么自然也就没办法获取future,所以为了future的获取更加灵活方便,就需要有这个promise对象。

主要方法有

图片

在这里插入图片描述

重点介绍如下两个常用的

1)get_future 用于返回一个和promise相关联的future对象,返回的这个future对象就可以调用相关get方法来获取promise通过se_value方法设置的值 

2)set_value 设置promise的对应值

案例代码

#include <iostream>       // std::cout
#include <functional>     // std::ref
#include <thread>         // std::thread
#include <future>         // std::promise, std::future
using namespace std;
void print_int (std::future<int>& fut) {
  cout<<"child thread  print_int"<<endl;
  int x = fut.get();
  std::cout << "value: " << x << '\n';
}

int main ()
{
  std::promise<int> prom;                      // create promise

  std::future<int> fut = prom.get_future();    // engagement with future

  std::thread th1 (print_int, std::ref(fut));  // send future to new thread

  std::this_thread::sleep_for(std::chrono::seconds(1));
  cout<<"main thread  prom.set_value (10)"<<endl;
  prom.set_value (10);                         // fulfill promise
                                               // (synchronizes with getting the future)
  th1.join();
  return 0;
}
执行结果:

图片

在这里插入图片描述

可以看到结果是子线程先打印了,执行到future.get会一直等待主线程进行set_value,才会继续往下执行,这里是不是相比前面async来说更加灵活,没有限制用啥多线程方式。

实战结合SurfaceFlinger剖析

构建好promise和对应future

图片

在这里插入图片描述

主线程通过future的get进行等待子线程执行完成通过set_value 主线程等待:

图片

在这里插入图片描述

子线程进行set_value部分

void SkiaGLRenderEngine::drawLayersInternal(
        const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
        const DisplaySettings& display, const std::vector<LayerSettings>& layers,
        const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/,
        base::unique_fd&& bufferFence) {
//省略
    resultPromise->set_value({NO_ERROR, std::move(drawFence)});
    return;
}

通过上面学习基础知识这个sf的这块代码看起来是不是很方便了,学习了个新c++语法立马就结合SurfaceFlinger源码部分实战分析

再次贴上perfetto、systrace来再次结合验证

图片

在这里插入图片描述

本文章对应视频手把手教你学framework: hal+perfetto+surfaceflingerhttps://mp.weixin.qq.com/s/LbVLnu1udqExHVKxd74ILg

图片

在这里插入图片描述

私聊作者+v(androidframework007)

七件套专题:

图片

在这里插入图片描述

点击这里 https://mp.weixin.qq.com/s/Qv8zjgQ0CkalKmvi8tMGaw

视频:重大消息:Hal+perfetto-systrace+SurfaceFlinger合集新专题发布-千里马android framework实战开发_哔哩哔哩_bilibili

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Linux_线程的同步与互斥
  • Elasticsearch基础(五):使用Kibana Discover探索数据
  • Mybatis——动态SQL常用标签
  • JavaWeb笔记_Cookie
  • 企业微信PC版应用跳转到默认浏览器,避坑指南,欢迎补充(Vue项目版)。。。
  • IVI(In-Vehicle Infotainment,智能座舱的信息娱乐系统)
  • 深度学习落地实战:人脸面部表情识别
  • 【Android Framewrok】Handler源码解析
  • L298N的输出电流与电压
  • 我在百科荣创企业实践——简易函数信号发生器(5)
  • CentOS Mysql8 数据库安装
  • 【NLP】关于参数do_sample的解释
  • php编译安装
  • 浅聊 Three.js 屏幕空间反射SSR-SSRShader
  • 【JavaScript 算法】滑动窗口:处理子数组问题
  • JavaScript 如何正确处理 Unicode 编码问题!
  • 【MySQL经典案例分析】 Waiting for table metadata lock
  • Apache Zeppelin在Apache Trafodion上的可视化
  • Git学习与使用心得(1)—— 初始化
  • QQ浏览器x5内核的兼容性问题
  • redis学习笔记(三):列表、集合、有序集合
  • SAP云平台里Global Account和Sub Account的关系
  • Spring Boot MyBatis配置多种数据库
  • 程序员该如何有效的找工作?
  • 从零搭建Koa2 Server
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 世界上最简单的无等待算法(getAndIncrement)
  • 小李飞刀:SQL题目刷起来!
  • NLPIR智能语义技术让大数据挖掘更简单
  • python最赚钱的4个方向,你最心动的是哪个?
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • ​​快速排序(四)——挖坑法,前后指针法与非递归
  • ​低代码平台的核心价值与优势
  • #NOIP 2014#Day.2 T3 解方程
  • (~_~)
  • (02)Unity使用在线AI大模型(调用Python)
  • (20)目标检测算法之YOLOv5计算预选框、详解anchor计算
  • (35)远程识别(又称无人机识别)(二)
  • (C#)获取字符编码的类
  • (pytorch进阶之路)扩散概率模型
  • (搬运以学习)flask 上下文的实现
  • (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  • (黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
  • (转) Android中ViewStub组件使用
  • .gitignore文件设置了忽略但不生效
  • .NET Core 实现 Redis 批量查询指定格式的Key
  • .Net MVC4 上传大文件,并保存表单
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .NET关于 跳过SSL中遇到的问题
  • @Builder注释导致@RequestBody的前端json反序列化失败,HTTP400
  • [2016.7.test1] T2 偷天换日 [codevs 1163 访问艺术馆(类似)]
  • [20170705]diff比较执行结果的内容.txt
  • [④ADRV902x]: Digital Filter Configuration(发射端)
  • [Codeforces1137D]Cooperative Game