•
标准库future分析
By Looning • 6 minutes read •
Future/Promise 机制
根据维基百科,Future 与 Promise 是成对存在的,形成一种值于其运算过程相互关联的机制。Future 代表在未来交付的一个值,你可以利用获取的 Future 来查看这个值是否就绪,也可以阻塞等待这个值完成。而Promise则是设置这个值的对象,使用 Promise 设置这个值后,就能够通过 Future 获取这个结果。需要注意的是,Promise 只能设置一次这个结果。
目前有很多语言都实现和支持了 Future/Promise 机制,不过不同语言之间多少还是有些区别。闲话少说,先介绍 C++ 标准库中的实现。
C++ 中的 Future/Promise
c++ 在 11 版本中就引入了这个机制,相关接口如下:
- promise:存储一个用于异步检索的值。
- packaged_task:封装一个函数以存储其返回值以供异步检索。
- future:等待异步设置的值。
- shared_future:等待一个异步设置的值(可能被其他 future 引用)。
- async:异步运行一个函数(可能在新线程中),并返回一个将保存结果的future。
- launch:指定 async 的启动策略。
- future_status:指定对 future 和 shared_future 执行的定时等待的结果。
下面简单介绍这些接口的用法和其内部实现。
promise
promise 的大致定义如下:
为了便于理解,此处对代码做了部分简化,后续的代码展示也是如此。
future_ 是一个共享指针,指向 State 类型,这个类型用于描述当前计算的状态。promise 和其对应的 future 共享同一个 State。
关于 State 的简要定义如下:
Result
是一个包装器,其内部包含Res
需要的内存空间和一个用来表示是否初始化的布尔值。使用这个是防止在计算完成前需要使用默认构造函数来初始化对应的Res
和Res
没有默认构造函数,并在出现异常时保存相关的异常指针。
成员函数
get_future
的实现比较简单,直接把共享指针传入 Future 类中构造一个新 Future 实例即可。
std::future<R>
set_value
则是设置对应的State::result_
,并利用State::once_
来实现一个 promise 只会设置一次 value 的目的。
set_value_at_thread_exit
与set_value
类似,但是设置的时机不是调用函数时,而是在线程退出时。 其实现机制大概是利用一个thread_local
的RAII
实例保存这些变量,然后在线程退出要析构这些实例时便能够通过析构函数设置对应的 value。
set_exception
与set_value
类似,但是不是设置对应的 value,而是设置异常。
set_exception_at_thread_exit
同上类似。
packaged_task
packaged_task
的实现与promise
类似,不过其内部成员不是State
,而是TaskState
。
packaged_task
与promise
不同之处在于,前者是一个可调用对象,其目的是封装一个可调用对象,然后进行异步调用,并将结果通过future
返回。
成员函数
valid
用于判断是否拥有一个共享状态。
get_future
获得当前共享状态对应的future
。
operator()
执行异步调用。
make_ready_at_thread_exit
执行异步调用,并在当前线程退出后才会设置共享状态为 ready。
reset
重置当前可调用对象和共享状态。
future
future
是在State
的基础上封装了一些跟 future 语义相关的接口而形成的类。其定义如下:
成员函数
share
用于将不可拷贝的future
切换成共享状态的shared_future
。
get
获取 future 对应的值,如果该值尚未就绪,则会利用State::retrieved_
条件变量阻塞等待。异步运行完成后会唤醒当前阻塞的线程。
valid
判断当前计算是否已完成。
wait
阻塞等待计算完成。
wait_for
阻塞等待一段时间,函数会返回一个future_status
枚举实例来说明等待结果。
wait_until
阻塞等待直到某个时间,函数会返回一个future_status
枚举实例来说明等待结果。
shared_future
与future
类似,但允许多个线程共享异步结果。
async
传入一个异步调用函数,根据launch
选择异步调用策略,并返回一个future
作为异步结果。
launch
决定async
的异步调用策略。
;
future_status
用于说明在调用wait_for
和wait_until
后的future
状态。
;
至此,我们完成了关于 c++ 标准库中 future 组件的实现的简要分析。