c++11 中的std::promise 与 std::future以及std::packaged_task的用法

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 章节

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奥特神龟3.1.4

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值