期值(future)
链接异步操作和异步操作的管道
创建一个异步调用,然后其他线程执行这个异步调用,异步调用执行完成之后会返回一个结果,然后异步调用创建方在需要使用这个异步调用结果的时候,通过某种方式来获取这个异步调用结果。C++
针对上述需求,提供了一些类对象来完成上面的过程,本文中讨论的C++
提供的两种对象为std::future
和std::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