如何从线程获取结果
首先可以传递结果引用给线程函数,这种方法需要额外的同步机制确定结果已经就绪;此外在C++11中可使用如下多种方法:std::async可以启动一个新线程并执行,其结果保存在返回的std::future中;std::packaged_task<>将一个future绑定到一个函数或可调用对象,当std::packaged_task<>对象被调用后将绑定的函数或可调用对象的返回值作为关联数据存储;将std::promise
std::future
获取
- get()
- 结果只能被取出一次,因此future可能处于valid或invalid状态
- get() 对返回值进行move assign操作
- 结果只能被取出一次,因此future可能处于valid或invalid状态
由于future的结果只能被取出一次,对同一future的多次get没有意义(是未定义操作),但可以使用std::shared_future
等待
- wait()
- wait_for()
- wait_until()
析构
如果调用析构函数的那个future是某一shared state的最后持有者,而相关的task已启动但尚未结束,析构函数会造成阻塞,直到任务结束
std::shared_future
- std::future调用share()方法得到
- 将std::future直接赋值得到
- 可多次调用get()
- 返回一个指向”存储于shared state“的值的const reference,需要注意悬挂引用
生成std::future的三种方式
- std::async
- std::promise
- std::packaged_task
std::async
在不需要立刻得到一个结果时,可以使用 std::async 来启动一个异步任务(在一个分享线程内)。std::async 返回一个 std::future 对象,它最终将持有函数的返回值。在 future 上调用 get() 方法(或 wait() 方法)可能会发生以下三种情况:
- func已经执行结束,立刻返回结果
- func已被启动但尚未结束,阻塞住直到func结束(future就绪)
- func尚未启动,强迫启动如同一个同步调用并阻塞等待
注意调用 std::async 并不保证传入的函数一定会被启动和结束,但生产环境中的高并发服务其操作系统底层平台都应该能够支持多线程的,所以我们主要关注能够实现并发的情况
调用形式
- future async (std::launch::async, F func, args...)
- 异步任务,创建一个并行线程
- future async (std::launch::deferred, F func, args...)
- 推迟任务,当对返回的future调用wait()或get()时被同步调用
- future async (F func, args...)
- 系统会根据当前形势选择两者之一的发射策略
参考上述future析构的情况,如果async返回的future未被使用的话,对此次async的调用将会阻塞直到func完成(返回的临时future调用析构函数会阻塞),即退化为同步调用
支持右值参数移动操作
std::packaged_task
std::packeaged_task<>周时持有目标函数及其可能的结果,典型的应用场景如:thread pool可控制何时运行以及多少个后台task同时运行
执行
- operator()
- 调用目标函数
设置值
- reset()
- make_ready_at_thread_exit()
获取future
- get_future()
- 只能调用一次
std::promise
std::async、std::packaged_task 都是在返回时将结果保存在std::future中,并且只返回一个future;对于那些不能以一个函数的方式通过调用它并获取返回值的任务,或者其结果来自多个地方时,可以使用std::promise
std::promise
设置值
-
set_value()
- 使相关联的future就绪
-
set_value_at_thread_exit()
- 为确保调用设置值的线程的local object及其他对象在”结果被处理前有所清除“,即令sahred state在线程结束时才就绪
获取future
- get_future()
- 只能调用一次
处理异常
待补充
参考
《C++标准库(第2版)》
《C++ Concurrency IN ACTION》