1 概述
Future允许异步访问特定提供程序设置的值的功能,这可能在不同的线程中。
这些提供程序(providers)中的每一个(要么是promise对象,要么是package_task对象,要么调用async)都与Future对象共享对共享状态的访问:提供程序使共享状态就绪的点与Future对象访问共享状态的点同步。
其类图如下:
1.1 promise
promise是一个对象,它可以存储T类型的值,以便将来的对象(可能在另一个线程中)检索,从而提供同步点。
在构造时,promise对象与一个新的共享状态相关联,它们可以在该状态上存储类型为T的值或从std::exception派生的异常。
这个共享状态可以通过调用成员get_future与Future对象相关联。调用后,两个对象共享相同的共享状态:
- promise对象是异步提供程序,并且期望在某个时刻设置共享状态的值。
- future对象是一个异步返回对象,它可以检索共享状态的值,并在必要时等待其准备就绪。
共享状态的生存期至少持续到与其关联的最后一个对象释放它或被销毁为止。因此,如果也与future相关,它可以在最初获得它的promise对象中生存下来。
1.2 packaged_task
packaged_task包装一个可调用元素,并允许异步检索其结果。
它类似于std::function函数,但会自动将其结果传输到未来的对象。
该对象内部包含两个元素:
- 一个存储的任务,它是一些可调用的对象(如函数指针、指向成员或函数对象的指针),其调用签名应采用Args…中类型的参数,并返回Ret类型的值。
- 一种共享状态,它能够存储调用存储任务(类型为Ret)的结果,并在将来异步访问。
共享状态通过调用成员get_future与未来的对象相关联。调用后,两个对象共享相同的共享状态:
- packaged_task对象是异步提供程序,期望通过调用存储的任务在某个时刻将共享状态设置为就绪。
- future对象是一个异步返回对象,它可以检索共享状态的值,并在必要时等待其准备就绪。
共享状态的生存期至少持续到与其关联的最后一个对象释放它或被销毁为止。因此,如果它也与future相关联,它可以在最初获得它的packaged_task对象中生存下来。
1.3 async
在某个时刻调用fn(以args作为参数),返回时无需等待fn的执行完成。
fn返回的值可以通过返回的future对象访问(通过调用其成员future::get)。
第二个版本(2)允许调用方选择特定的启动策略,而第一个版本(1)使用自动选择,就好像调用(2)时将launch::async|launch::deferred 为策略一样。
该函数在共享状态下临时存储使用的线程处理程序或fn和args的衰变副本(作为延迟函数),而不准备就绪。一旦fn的执行完成,共享状态就包含fn返回的值,并准备就绪。
策略:
- launch::async 异步:启动一个新线程来调用fn(就好像一个线程对象是用fn和args作为参数构建的,访问返回的future的共享状态会加入它)。
政策 - launch::deferred 对fn的调用被延迟,直到访问返回的future的共享状态(使用wait或get)。此时,将调用fn,并且不再将该函数视为已延迟。当这个调用返回时,返回的future的共享状态就准备好了。
- launch::async|launch::deferred 自动:该功能自动选择策略(在某个时刻)。这取决于系统和库的实现,通常会针对系统中当前的并发可用性进行优化。
2 使用实例
2.1 promise
struct Function4Promise
{
int value = -1;
void print(std::future<int> &f)
{
try
{
value = 0;
value = f.get();
std::cerr << "value: " << value << std::endl;
}
catch(const std::exception& e)
{
value = -1;
std::cerr << e.what() << '\n';
}
}
void set_value(std::promise<int> &p , int index)
{
try
{
std::vector<int> a {
1, 2, 3, 4, 5 };
p.set_value(a.at(index));
}
catch(const std::exception& e)
{
p.set_exception(std::current_exception());
}
}
void set_value_at_thread_exit(std::promise<int> &p, int index)
{
try
{
std::vector<int> a {
1, 2, 3, 4, 5 };
p.set_value_at_thread_exit(a.at(index));
}
catch(const std::exception& e)
{
p.set_exception_at_thread_exit(std::current_exception());
}
}
};
void FutureSuite::promise_set_value()
{
std::promise<int> p;
std::future<int> f = p.get_future();
Function4Promise function;
std::thread thread(&Function4Promise::print, std::ref(function), std::ref(f));
std::this_thread::sleep_for(std::chrono::seconds(1));
TEST_ASSERT_EQUALS(true, function