上一篇文章中讲到, 我打算写一篇文章,聊聊async
、packaged_task
和promise
的区别。所以现在我就来填坑了。
TL;DR
- async:提供最高层次的抽象。如果你不需要控制线程的运行时机,就选这个。
-
packaged_task:抽象层次比
async
低。如果你需要控制线程的运行时机,且线程执行的结果即目标结果时,选这个。 - promise:抽象层次最低。当你想在线程中设置目标结果的值,选这个。
如果你想了解得更详细,那就继续往下看吧。
同台竞技
async
、packaged_task
和promise
三者有一个共同点:它们都可以返回一个future
对象,用户可以通过这个future
的get
方法获取最终的结果。
在下面的代码中,分别用这三者实现同样的功能:延时2秒后返回0:
#include <chrono>
#include <future>
#include <iostream>
#include <thread>
int main()
{
std::packaged_task<int()> task([](){
std::chrono::milliseconds dura( 2000 );
std::this_thread::sleep_for( dura );
return 0;
});
std::future<int> f1 = task.get_future();
std::thread(std::move(task)).detach();
std::future<int> f2 = std::async(std::launch::async, [](){
std::chrono::milliseconds dura( 2000 );
std::this_thread::sleep_for( dura );
return 0;
});
std::promise<int> p;
std::future<int> f3 = p.get_future();
std::thread([](std::promise<int> p){
std::chrono::milliseconds dura( 2000 );
std::this_thread::sleep_for( dura );
p.set_value(0);
},
std::move(p)).detach();
std::cout << "Waiting..." << std::flush;
f1.wait();
f2.wait();
f3.wait();
std::cout << "Done!\nResults are: "
<< f1.get() << " " << f2.get() << " " << f3.get() << "\n";
return 0;
}
从上面这段代码可以看到,这三者分别工作在不同的抽象层次上。
-
async
层次最高,你只需要给它提供一个函数,它就会返回一个future
对象。接下来就只需等待结果了。 -
packaged_task
次之,你在创建了packaged_task
后,还要创建一个thread
,并把packaged_task
交给它执行。 -
promise
就最低了。在创建了thread
之后,你还要把对应的promise
作为参数传入。这还没完,别忘了在函数中手动设置promise
的值。
那么我们的第一个结论就很清晰了:async
抽象层次最高,所以除非你需要对并发过程进行细粒度的控制(比如在一些场合下),优先使用async
来执行异步任务。
那么什么属于是“一些场合”呢?
async VS. packaged_task and promise
前面已经看到,async
会接收一个函数,并返回一个future
。在默认情况下,该函数会被就地执行。这也许不是你想要的。通过传递std::launch::defer
,可以修改为直到调用future.get
才开始执行async
中的函数。
即使这样,如果你想把执行函数的时机和获取future对象的时机分离,最好还是放弃用async
,而是使用更为底层的packaged_task
和promise
。
BTW,async
有一个古怪的特性,如果你把async
返回的future
赋值给一个临时变量(或者没管它的返回值),当该变量生命周期结束时,程序会一直阻塞直到async
中的函数执行完毕。
{
std::future<int> tmp = std::async(std::launch::async, [](){
std::chrono::milliseconds dura(VERY_LONG_TIME);
std::this_thread::sleep_for(dura);
return 0;
});
// block here for VERY_LONG_TIME
}
这种意料之外的行为会在C++14中被取消掉。所以你用的编译器可能不会遇到这问题。
packaged_task VS. promise
剩下的两个之中怎么选呢?
promise
的层次比packaged_task
低,所以promise
提供给用户的控制粒度也比packaged_task
要细。因此,如果你想要更彻底的控制,就选择promise
吧。
promise
几乎就是future
的另一半。对promise
调用set_value
,就如同对future
调用set_value
。比起packaged_task
,promise
并不在意函数的返回值——毕竟它的值需要手动调用set_value
进行设置。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。