c++11 中的std::promise 与 std::future以及std::packaged_task的用法
前言
关于 std::promise和std::future 和 std::packaged_task 我在多线程 项目中使用到,对与这两个 类模板,所以对于这三个类模板的用法进行总结
std::future
future 说明
std::future提供了一种访问异步操作结果的机制。一个异步操作我们是不可能马上就获取操作结果的,只能在未来某个时候获取,但是我们可以以同步等待的方式来获取结果,可以通过查询future的状态(future_status)来获取异步操作的结果。future_status有三种状态:
- deferred:异步操作还没开始
- ready:异步操作已经完成
- timeout:异步操作超时
线程可以周期性的在这个future上等待一小段时间,检查future是否已经ready,如果没有,该线程可以先去做另一个任务,一旦future就绪,该future就无法复位(无法再次使用这个future等待这个事件),所以future代表的是一次性事件。
类模板 std::future
提供访问异步操作结果的机制:
- (通过 std::async 、 std::packaged_task 或 std::promise创建的)异步操作能提供一个
std::future
对象给该异步操作的创建者。
-
然后,异步操作的创建者能用各种方法查询、等待或从
std::future
提取值。若异步操作仍未提供值,则这些方法可能阻塞。 -
异步操作准备好发送结果给创建者时,它能通过修改链接到创建者的
std::future
的共享状态(例如 std::promise::set_value)进行。
注意, std::future
所引用的共享状态不与另一异步返回对象共享(与 std::shared_future 相反)。
future的类型
在****库的头文件中声明了两种future,唯一future(std::future)和共享future(std::shared_future)这两个是参照std::unique_ptr和std::shared_ptr设立的,前者的实例是仅有的一个指向其关联事件的实例,而后者可以有多个实例指向同一个关联事件,当事件就绪时,所有指向同一事件的std::shared_future实例会变成就绪。
future的使用
std::future是一个模板,例如std::future<int>
,模板参数就是期待返回的类型,虽然future被用于线程间通信,但其本身却并不提供同步访问,我们必须通过互斥元或其他同步机制来保护访问。
future使用的时机
当你不需要立刻得到一个结果的时候,你可以开启一个线程帮你去做一项任务,并期待这个任务的返回
std::async返回一个std::future对象,而不是给你一个确定的值(所以当你不需要立刻使用此值的时候才需要用到这个机制)。当你需要使用这个值的时候,对future使用get(),线程就会阻塞直到future就绪,然后返回该值。
std::future 由std::async 函数 创建
#include <future>
#include <iostream>
int sum(int a, int b)
{
std::cout << "running the threadFunc" << std::endl;
return a + b;
}
void printHello()
{
std::cout << "Hello World" << std::endl;
}
int main()
{
std::future<int> result = std::async(std::launch::deferred,sum, 1, 1);
printHello();
std::cout << result.get() << std::endl;
return 0;
}
std::future 由 std::packaged_task 创建
#include <future>
#include <iostream>
int sum(int a, int b)
{
std::cout << "running the threadFunc" << std::endl;
return a + b;
}
int max(int a, int b)
{
return a > b ? a : b;
}
void printHello()
{
std::cout << "Hello World" << std::endl;
}
int main()
{
//两种绑定方式
std::packaged_task<int(int, int)> task1(max);
std::packaged_task<int()> task2(std::bind(sum,1,1));
std::future<int> result1 = task1.get_future();
std::future<int> result2 = task2.get_future();
task1(1,2);
task2();
printHello();
std::cout << "task1 result:" << result1.get() << std::endl;
std::cout <<"task2 result:" << result2.get() << std::endl;
return 0;
}
std::future 由 std::promise 创建
#include <future>
#include <iostream>
#include<chrono>
int sum(std::promise<int>& p ,int a, int b)
{
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "running the threadFunc" << std::endl;
p.set_value(a+b);
return a + b;
}
void printHello()
{
std::cout << "Hello World" << std::endl;
}
int main()
{
std::promise<int> p;
std::future<int> result = p.get_future();
std::thread t(sum, std::ref(p), 1, 1);
printHello();
std::cout << result.get() << std::endl;
t.join();
return 0;
}
std::future 成员函数一览
template<typename ResultType>
class future
{
public:
future() noexcept;
future(future&& other) noexcept;
future(future const&) = delete;
~future();
future& operator=(future&& other) noexcept;
future& operator=(future const&) = delete;
bool valid() const noexcept;
ResultType get();
shared_future<ResultType> share();
void wait();
template<typename Rep,typename Period>
future_status wait_for(
std::chrono::duration<Rep,Period> const& relative_time);
template<typename Clock,typename Duration>
future_status wait_until(
std::chrono::time_point<Clock,Duration> const& absolute_time);
};
构造函数
-
默认构造函数。构造无共享状态的
std::future
。构造后, valid() == false 。 -
移动构造函数。用移动语义,以
other
的共享状态构造std::future
。构造后 other.valid() == false 。 -
std::future 不可复制构造。
析构函数
释放任何共享状态。这表示
- 若当前对象持有其共享状态的最后一个引用,则销毁共享状态;
- 当前对象放弃其共享状态的引用;
而这些操作不会阻塞共享状态变为就绪的过程,除了若以下条件全为真,则这些操作可以阻塞: 共享状态是由对 std::async 的调用创建的, 共享状态尚未就绪,并且 当前对象是到共享状态的最后一个引用。 | (C++14 起) |
---|---|
实践中,仅当任务的运行策略为 std::launch::async 时这些操作才会阻塞(见 “Effective Modern C++” Item 36),因为运行时系统选择这么做,或者说在 std::async 调用中规定如此。
operator=
赋值另一 future 对象的内容。
-
释放任何共享状态并移动赋值
other
的内容给 *this 。赋值后, other.valid() == false 且 this->valid()将产生与 other.valid() 在赋值前相同的值。 -
std::future 不可复制赋值。
share
转移 *this 的共享状态,若存在,到 std::shared_future对象可引用同一共享对象,这对于 std::future 不可能。
在 std::future 上调用 share
后 valid()== false 。
get
get
方法等待直至 future
拥有合法结果并(依赖于使用哪个模板)获取它。它等效地调用 wait()等待结果。
泛型模板和二个模板特化各含单个 get
版本。 get
的三个版本仅在返回类型有别。
若调用此函数前 valid()为 false 则行为未定义。
建议实现在调用前检测 valid() 为 false 的情况,并抛出以 std::future_errc::no_state为 error_condition 的 std::future_error。
释放任何共享状态。调用此方法后 valid()为 false 。
vaild
检查 future 是否拥有共享状态
wait
阻塞直至结果变得可用。调用后 valid() == true 。
若调用此函数前 valid
() == false 则行为未定义。
建议实现在调用前检测 valid() 为 false 的情况,并抛出以 std::future_errc::no_state为 error_condition 的 std::future_error。
wait_for
等待结果变得可用。阻塞直至经过指定的 timeout_duration
,或结果变为可用,两者的先到来者。返回值鉴别结果的状态。
此函数可能由于调度或资源争议延迟而阻塞长于 timeout_duration
。
推荐标准库用稳定时钟度量时长。若实现用系统时钟代替,则等待时间可能也对时钟调整敏感。
若调用此函数前 valid()== false 则行为未定义。
建议实现在调用前检测 valid() 为 false 的情况,并抛出以 std::future_errc::no_state为 error_condition 的 std::future_error。
wait_until
wait_until
等待结果变为可用。它阻塞直至抵达指定的 timeout_time
,或结果变为可用,两者的先到来者。返回值指示 wait_until
为何返回。
若调用此函数前 valid() 为 false ,或 Clock
不符合时钟 (Clock) 要求则行为未定义
建议实现在调用前检测 valid() 为 false 的情况,并抛出以 std::future_errc::no_state为 error_condition 的 std::future_error。
std::promise
std::promise的拷贝构造函数是被删除了的,所以std::promise作为函数的参数时,必须用std::ref(pro)
从字面意思上理解promise代表一个承诺。promise比std::packaged_task抽象层次低。
std::promise<T>
提供了一种设置值的方式,它可以在这之后通过相关联的std::future<T>
对象进行读取。换种说法,之前已经说过std::future可以读取一个异步函数的返回值了,那么这个std::promise就提供一种方式手动让future就绪。
在std::promise的对象上,调用set_value后,future变成就绪(ready)状态,调用future对象的get方法的地方就由阻塞变为不阻塞了
std::packaged_task
类模板std::packaged_task包装任何可调用目标(函数、lambda表达式、绑定表达式或其他函数对象),以便可以异步调用它。它的返回值或抛出的异常存储在共享状态中,可以通过std::future对象访问该状态。
可以通过std::packaged_task对象获取任务相关联的特点,调用get_future()方法可以获得std::packaged_task对象绑定的函数的返回值类型的future。std::packaged_task的模板参数是函数签名
示例可见future 章节