std::async,c++11中提供的异步任务高级抽象,包含在 <future>头文件中,能让你方便的实现异步地执行一个任务
并在需要地时候获取其结果。和直接使用std::thread 相比,有着一些优势:
1.std::async 返回的future对象,可以方便地等待callable对象执行完成并获取其返回值
2.能从实现库的一些高级功能中获益,比如线程池等,并大大减少异常的产生。
当然其使用更简洁。但是async存在着一些问题,使用的时候要小心。
1.async的启动策略,两种启动策略 std::launch::async , std::launch::deferred
如果不指定,将采用默认的启动策略,等同于 launch::async | launch::deferred
并且标准中没有规定默认采用哪种策略,有实现库自己决定,测试VS2013中默认方式为async而gcc4.8.2为deferred。
在使用时就要注意。如果为launch::deferred模式,那么永远不会启动新线程去执行,直到你调用 wait 或 get时
才在你的当前线程中去执行callable对象。如果你需要让callable立即启动线程去执行,那么一定要
在调用async时指定launch::async,可以使用一个简单的包装避免忘记:
template <typename F, typename... Args>
inline auto real_async(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type>
{
return std::async(std::launch::async,
std::forward<F>(f),
std::forward<Args>(args)...);
}
2.std::async返回的future对象
以下代码存在一个问题
void work_proc()
{
vector<int> numbers;
... //do something
real_async(some_proc, std::move(numbers));
}
在当前线程中调用了work_proc,希望让单独的一个线程去处理一些不关心结果的耗时的事情,然后
当前线程再去处理一些其他的任务。然而事情不一定如你期待那样。实际上在vs2013上一切表现如
预期,在gcc4.8.2测试,会一直阻塞在work_proc中,直到some_proc执行完成。
在标准库中有说明,在std::future的析构函数中:
~future();
| (since C++11) | |
Releases any shared state. This means
- if the return object or provider holds the last reference to its shared state, the shared state is destroyed; and
- the return object or provider gives up its reference to its shared state; and
|
注意第三条:在以下三个条件全部满足时future析构可能阻塞
(1)共享状态是由调用std::async时创建的
(2)共享状态还不是ready状态
(3)被析构的当前对象持有着对共享状态的最后一个引用
而work_proc函数中的情况刚好全部满足。然而不幸的是如果你仍然想用async,并且忽略其返回,是没办法避免阻塞的。
但是还是有办法实现相同的目的,使用std::packaged_task和std::thread.
template <typename F, typename... Args>
auto really_async2(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type>
{
using _Ret = typename std::result_of<F(Args...)>::type;
auto _func = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
std::packaged_task<_Ret()> tsk(std::move(_func));
auto _fut = tsk.get_future();
std::thread thd(std::move(tsk));
thd.detach();
return _fut;
}