future与shared_future
上一篇博文介绍关于future
的一些基本应用与注意事项,还简单介绍std::shared_future
类接收future类模板的share()
成员函数移动语义给std::shared_future
类对象,可以多次调用get()
函数。下面是std::shared_future
类模板的相关声明与定义,可以发现有拷贝构造与拷贝运算。
template <class _Ty>
class shared_future : public _State_manager<_Ty> { // class that defines a copyable asynchronous return object
// that holds a value
using _Mybase = _State_manager<_Ty>;
public:
shared_future() noexcept { // construct
}
shared_future(const shared_future& _Other) noexcept : _Mybase(_Other) { // construct from shared_future object
}
shared_future& operator=(const shared_future& _Right) noexcept { // assign from shared_future object
_Mybase::operator=(_Right);
return *this;
}
shared_future(future<_Ty>&& _Other) noexcept
: _Mybase(_STD forward<_Mybase>(_Other)) { // construct from rvalue future object
}
shared_future(shared_future&& _Other) noexcept
: _Mybase(_STD move(_Other)) { // construct from rvalue shared_future object
}
shared_future& operator=(shared_future&& _Right) noexcept { // assign from shared_future rvalue object
_Mybase::operator=(_STD move(_Right));
return *this;
}
~shared_future() noexcept { // destroy
}
const _Ty& get() const { // block until ready then return the stored result or
// throw the stored exception
return this->_Get_value();
}
};
下面我们对shared_future
类模板做个小结:
- 可以显示or隐式的获取future对象的所有权,调用方法如下:
std::future<int> fut = std::async(get_value);
// 1. fut.share()共享之后, fut的内容权限传递给shfut,fut为empty.
// 2. 传递给共享对象之后,可以多次调用访问.
//std::shared_future<int> shfut = fut.share(); // 显示获取对象所有权
std::shared_future<int> shfut(std::move(fut)); // 隐式获取future对象所有权
- shared_future可以多次调用
get()
成员函数,是否释放为当其关联的计数为0时候,而future执行get()
一次之后,就将所有权转交出去,不能够多次调用。
std::promise的使用
下面我们通过一个示例程序来介绍一下std::promise
的功能使用:
#include <iostream> // std::cout
#include <functional> // std::ref
#include <thread> // std::thread
#include <future> // std::promise, std::future
class Student
{
public:
Student() {
age_ = 0;
name_ = "empty";
score_ = 0;
}
Student(int age, std::string name, float score)
:age_(age), name_(name), score_(score)
{}
~Student() = default;
public:
int age_;
std::string name_;
float score_;
};
// 通过promise来给线程传入参数
void print_int(std::future<Student>& fut)
{
std::cout << "start thread runs ..." << std::endl; // ①
Student stu = fut.get(); // ②
std::cout <<"name: " << stu.name_ << " age: " << stu.age_
<< " score: " << stu.score_ << std::endl; // ③
}
int main()
{
std::promise<Student> prom; // create promise
std::future<Student> fut = prom.get_future(); // engagement with future
std::thread th1(print_int, std::ref(fut)); // ④
std::this_thread::sleep_for(std::chrono::seconds(2)); // 2s sleep
Student stu(10, "Kai", 99.5);
prom.set_value(stu); // ⑥
th1.join(); // ⑦
return 0;
}
下面对上述代码进行分析:程序执行流程为:④ → \rightarrow → ① → \rightarrow → ② → \rightarrow → ⑥ → \rightarrow → ⑦ → \rightarrow → ③了解了执行流程后,我们来看一下关于上面这个程序要说明的功能:
- 输入的Student类作为结构体输入future类模板,更加便利使用;future类模板不仅支持原始类型还支持特化模式;
- 通过使用std::promise类对象的
get_future()
函数与future对象绑定到一起; - 启动线程,但是函数内部传入future调用
get()
函数需要等待输入参数; - 通过std::promise类的
set_value()
函数从外部设置参数,执行传入到线程函数里面; - 线程join()阻塞等待线程函数执行完毕;
上述一个小程序简短说明std::promise类目标的作用,可以与某个future对象关联,共享某个状态值,通过set_value来进行共享值的传递。
std::packaged_task的使用
关于std::packaged_task
类模板主要是包装可调用的对象,可以是函数指针、成员函数指针、函数对象等;其次是能够保存任务的返回值,共享状态机制通过std::future
来接收。我们来看下面的示例程序:
#include <iostream>
#include <future>
#include <chrono>
#include <thread>
int countdown(int from, int to)
{
for (int i = from; i != to; --i)
{
std::cout << i << "\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
}
std::cout << "Lift off!\n";
return from - to;
}
int main(void)
{
std::packaged_task<int(int, int)> tsk(countdown); // set up pachaged_task
std::future<int> ret = tsk.get_future();
std::thread th(std::move(tsk), 10, 0);
int value = ret.get();
std::cout << "The countdown lasted for " << value << " seconds.\n";
th.join();
system("pause");
return 0;
}
上面的一个带有入参和返回值的函数,通过std::packaged_task类模板传入,通过std::future来接收异步返回值。std::packaged_task的共享状态生命周期将会持续到最后一个与之相关的对象被释放或者销毁。
std::async的使用
关于异步线程函数std::async()
能够比较便捷的获取线程函数的返回值,通过std::future对象来保存。关于std::async()函数的第一个参数下面的代码注释里面有做介绍,这里简短说下:
- std::launch::async 参数代表异步函数std::async()立即执行;
- std::launch::deferred 参数代表异步函数std::async()延迟执行,当调用
wait()
或者get()
执行; - 默认情况下为std::launch::async | std::launch::deferred 依赖系统或者库函数第三方应用在某一个运行;
#include <iostream>
#include <chrono>
#include <thread>
#include <future>
#include <mutex>
bool is_prime(int x)
{
std::cout << "Calculating, please wait...\n";
for (int i = 2; i < x; ++i)
if (0 == x % i)
return false;
return true;
}
int main(void)
{
// call is_prime(313222313) asynchronously:
// std::launch::async 立即执行
// std::launch::deferred 延迟执行,直到调用get()函数
// std::launch::async | std::launch::deferred 缺省默认方式,根据系统
std::future<bool> fut = std::async(std::launch::deferred, is_prime, 313222313);
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Checking whether 313222313 is prime.\n";
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";
system("pause");
return 0;
}
上述代码中如果你将std::async()
异步函数第一个参数设置为std::launch::async
表示立即执行,输出终端顺序可能有些不太一样。但是,如果你设置延迟执行std::launch::deferred
,不论你在主线程是否设置延迟函数,它在调用get()
函数时刻才开始执行。
小结
好了,看到这里不知道你有没有一些疑惑?1. 为什么感觉std::promise能够传入数据,共享状态,std::packaged_task同样可以,并且似乎可以接受模板的方式入参,std::async也可以,为什么需要这么多的功能?其实主要是下面这样的应用:
- std::promise主要是在异步线程中获取某个值,
get_future()
只能够调用一次; - std::packaged_task主要是获取异步线程中的返回值;
- std::async主要是更加方便的使用,它不需要显示声明
std::thread
对象,更加方便;