1.简介
C++11标准库提供了std::future和std::promise用于处理异步任务。std::future表示期望值,可以使用期望值等待一次性事件。std::promise表示承诺的意思,std::future可以和std::promise进行绑定。std::promise<T>提供设定值的方式(类型为T),这个类型和std::future<T>对象相关联,期望值可以阻塞等待线程,同时,提供数据的线程可以使用组合中的承诺值来对相关值进行设置,并且将期望值的状态设置为就绪状态。
2.std::future
2.1 std::future::get()
std::future::get()是用来获取std::future对象的值或者异常。如果异步任务还未执行完成,get()会阻塞当前线程,直到任务完成,如果任务已经完成,则get()会立即返回。get()函数只能调用一次,它会移动或消耗掉 std::future对象的状态。一旦 get()被调用,std::future
对象就不能再被用来获取结果。
2.2 std::future::wait()
std::future::wait()
调用的时候同样会阻塞当前线程,但是不会得到任务的返回结果,仅仅等待异步任务完成,异步任务完成会立即返回,wait()可以多次调用, 不会消耗std::future对象的状态
2.3 示例
2.3.1 std::future::get()
使用std::async可以启动一个异步任务,跟std::thread对象等待的方式不同,std::async会返回一个std::future<T>对象,这个对象包含异步任务指向的结果(异步任务执行函数的返回值),当需要这个结果的时候,调用std::future对象的get()函数即可,get()函数阻塞线程,直到期望值状态变为就绪。
int do_async_thing(int nData)
{
std::cout << "thread_id:" << std::this_thread::get_id() <<std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
return nData * nData;
}
int main()
{
//std::future保存的int类型的数据,该值对应do_async_thing任务的返回值
std::future<int> _future = std::async(std::launch::async,do_async_thing,10);
//调用get()会阻塞线程,直到执行完do_async_thing
std::cout << _future.get() << std::endl; //结果为100
//异步任务执行完成后才会输出主线程的id
std::cout << "main_id:" << std::this_thread::get_id() << std::endl;
return 0;
}
2.3.2 std::future::wait()
可以使用wait()去等待任务执行完成,等到执行完成后,然后直接调用get()去获取任务执行的结果
int do_async_thing(int nData)
{
std::cout << "thread_id:" << std::this_thread::get_id() <<std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
return nData * nData;
}
int main()
{
//std::future保存的int类型的数据,该值对应do_async_thing任务的返回值
std::future<int> _future = std::async(std::launch::async,do_async_thing,10);
//调用wait()会阻塞线程,直到执行完do_async_thing
_future.wait();
std::cout << _future.get() << std::endl; //结果为100
//异步任务执行完成后才会输出主线程的id
std::cout << "main_id:" << std::this_thread::get_id() << std::endl;
return 0;
}
2.3.3 std::future::wait_for()
除了使用get()和wait()来检测异步任务是否完成,也可以通过wait_for()或者wait_until()来检测在一定的时间内,异步任务是否已经完成,在一些对时序要求较高的程序中,会通过wait_for等来检测任务是否在规定时间完成,如果完成了就正常往下执行,如果没有则走异常流程,确保整个流程执行的时间满足时序要求。
使用wait_for()或者wait_until()方法会返回一个状态std::future_status,当状态为就绪(ready)的时候,表示任务已经完成,如果状态为超时(timeout)的时候,表示在规定时间内未完成任务
上述代码改成wait_for()来等待完成
int do_async_thing(int nData)
{
std::cout << "thread_id:" << std::this_thread::get_id() <<std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
return nData * nData;
}
int main()
{
std::future<int> _future = std::async(std::launch::async,do_async_thing,10);
std::future_status _status = _future.wait_for(std::chrono::milliseconds(500));
//输出timeout,任务在500ms之内未完成
if (_status == std::future_status::timeout)
{
std::cout << "timeout " << std::endl;
}
//如果在规定的时间内完成了任务则状态会变成ready
else if(_status == std::future_status::ready)
{
std::cout << _future.get() << std::endl;
}
std::cout << "main_id:" << std::this_thread::get_id() << std::endl;
return 0;
}
2.3.4 std::future::wait_until()
wait_until的用法跟wait_for()用法类似,将上述代码改成wait_until()的形式
int do_async_thing(int nData)
{
std::cout << "thread_id:" << std::this_thread::get_id() <<std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
return nData * nData;
}
int main()
{
std::future<int> _future = std::async(std::launch::async,do_async_thing,10);
//获取当前时间点
auto start = std::chrono::steady_clock::now();
//从当前时间点后的500ms
auto end = start + std::chrono::milliseconds(500);
//等待异步任务完成或者达到end的时间节点
std::future_status _status = _future.wait_until(end);
//输出timeout
if (_status == std::future_status::timeout)
{
std::cout << "timeout " << std::endl;
}
else if(_status == std::future_status::ready)
{
std::cout << _future.get() << std::endl;
}
std::cout << "main_id:" << std::this_thread::get_id() << std::endl;
return 0;
}
3.std::promise
std::promise可以在某一线程中去设置值或者异常,std::future在另一个线程可以去或者这个值或者异常。可以通过给定的一个std::promise的get_future()来获取与之相关的std::future对象。
3.1 std::set_value
std::promise可以通过set_value来设置promise的值,然后可以通过std::future的get()来获取设置的这个值,调用get()获取值的时候,如果promise的值还未被设置,则会阻塞当前线程。
int do_some_thing(std::promise<int> promise)
{
int nData = 100;
//设置promise的值
promise.set_value(nData);
return nData;
}
int main()
{
//创建一个promise对象
std::promise<int> _promise;
//创建一个std::future对象,该对象与_promise对象关联
std::future<int> _future = _promise.get_future();
std::thread t(do_some_thing,std::move(_promise));
//获取_promise设置的值,结果输出100
std::cout << _future.get() << std::endl;
t.join();
return 0;
}
3.2 set_exception
除了通过set_value来设置promise的值,也可以通过set_exception来设置异常值
void set_exception(std::promise<void> promise)
{
try
{
//抛出异常,runtime_error表示只有在运行时才能检测的的问题
//继承自std::exception
throw runtime_error("run error");
}
catch (...)
{
//current_exception:当前正在传播的异常
promise.set_exception(std::current_exception());
}
}
int main()
{
std::promise<void> _promise;
std::future<void> _future = _promise.get_future();
std::thread t(set_exception,std::move(_promise));
//获取异常
try
{
std::cout << "wait" << std::endl;
_future.get();
}
catch (const std::exception &e)
{
std::cout << e.what() << std::endl;
}
t.join();
//输出wait然后再出书run error
return 0;
}
4.std::future和std::promise示例
两个模块,模块1生产数据,然后将数据传递给模块2,等待模块2在规定时间内完成,然后返回结果,示例如下:
struct Data;
std::queue<std::shared_ptr<Data>> g_queue;
std::mutex g_mtx;
std::condition_variable g_cv;
struct Data
{
Data()
{
m_sPtr = std::make_shared<std::promise<int>>();
}
Data(const Data& other)
{
m_sPtr = other.m_sPtr;
}
Data &operator = (const Data &other)
{
if (this != &other)
{
m_sPtr = other.m_sPtr;
}
return *this;
}
std::shared_ptr<std::promise<int>> m_sPtr;
};
void Producer()
{
while (true)
{
//创建数据
auto data = std::make_shared<Data>();
//将std::future对象和std::promise绑定,通过promise的get_future来获取
auto future = data->m_sPtr->get_future();
{
//数据塞入队列
std::unique_lock<std::mutex> locker(g_mtx);
g_queue.push(data);
g_cv.notify_one();
}
//再规定时间内等待结果
auto status = future.wait_for(std::chrono::milliseconds(10));
if (std::future_status::timeout == status)
{
std::cout << "timeout" << std::endl;
break;
}
else if (std::future_status::ready == status)
{
std::cout << "value:" << future.get() << std::endl;
}
}
}
void Consumer()
{
static int s_nCount = 1;
while (true)
{
std::shared_ptr<Data> data;
{
std::unique_lock<std::mutex> locker(g_mtx);
g_cv.wait(locker, [&]()
{
return !g_queue.empty();
});
data = g_queue.front();
g_queue.pop();
}
//设置值
data->m_sPtr->set_value(s_nCount++);
}
}
int main()
{
std::thread t1(Producer);
std::thread t2(Consumer);
t1.join();
t2.join();
return 0;
}