C++ 中的 task based 并发
TL;DR
async:提供最高层次的抽象。如果你不需要控制线程的运行时机,就选这个。
packaged_task:抽象层次比
async
低。如果你需要控制线程的运行时机,且线程执行的结果即目标结果时,选这个。promise:抽象层次最低。当你想在线程中设置目标结果的值,选这个。
如果你想了解得更详细,那就继续往下看吧。
同台竞技
async
、packaged_task
和promise
三者有一个共同点:它们都可以返回一个future
对象,用户可以通过这个future
的get
方法获取最终的结果。
在下面的代码中,分别用这三者实现同样的功能:延时 2 秒后返回 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
中的函数执行完毕。
这种意料之外的行为会在 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
进行设置。
评论