packaged_task和async有什么区别


134

在使用C ++ 11的线程模型时,我注意到

std::packaged_task<int(int,int)> task([](int a, int b) { return a + b; });
auto f = task.get_future();
task(2,3);
std::cout << f.get() << '\n';

auto f = std::async(std::launch::async, 
    [](int a, int b) { return a + b; }, 2, 3);
std::cout << f.get() << '\n';

似乎做的完全一样。我知道,如果std::async与一起跑步,可能会有很大的不同std::launch::deferred,但是在这种情况下,有没有区别?

这两种方法之间有什么区别,更重要的是,我应该在哪种用例中使用一种而不是另一种?

Answers:


161

实际上,您刚才给出的示例显示了使用较长函数(例如,

//! sleeps for one second and returns 1
auto sleep = [](){
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return 1;
};

打包任务

A packaged_task不会自己开始,您必须调用它:

std::packaged_task<int()> task(sleep);

auto f = task.get_future();
task(); // invoke the function

// You have to wait until task returns. Since task calls sleep
// you will have to wait at least 1 second.
std::cout << "You can see this after 1 second\n";

// However, f.get() will be available, since task has already finished.
std::cout << f.get() << std::endl;

std::async

另一方面,std::asyncwith launch::async将尝试在另一个线程中运行任务:

auto f = std::async(std::launch::async, sleep);
std::cout << "You can see this immediately!\n";

// However, the value of the future will be available after sleep has finished
// so f.get() can block up to 1 second.
std::cout << f.get() << "This will be shown after a second!\n";

退税

但是在尝试使用async所有功能之前,请记住,返回的未来具有特殊的共享状态,该状态要求future::~future阻止:

std::async(do_work1); // ~future blocks
std::async(do_work2); // ~future blocks

/* output: (assuming that do_work* log their progress)
    do_work1() started;
    do_work1() stopped;
    do_work2() started;
    do_work2() stopped;
*/

因此,如果您想要真正的异步,则需要保留return future,或者如果情况发生变化,则不关心结果:

{
    auto pizza = std::async(get_pizza);
    /* ... */
    if(need_to_go)
        return;          // ~future will block
    else
       eat(pizza.get());
}   

有关此问题的更多信息,请参见Herb Sutter的文章async~future,它们描述了问题,而Scott Meyer的std::futuresfrom std::async不是特别的,它描述了见解。另请注意,此行为是在C ++ 14及更高版本中指定的,但通常也在C ++ 11中实现。

进一步的差异

通过使用,std::async您无法再在特定线程上运行任务,而std::packaged_task可以将其移至其他线程。

std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::thread myThread(std::move(task),2,3);

std::cout << f.get() << "\n";

另外,packaged_task在调用之前需要调用a f.get(),否则您的程序将冻结,因为将来永远都无法准备好:

std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::cout << f.get() << "\n"; // oops!
task(2,3);

TL; DR

使用std::async,如果你想一些事情做,他们就完成了的时候真的不关心,而且std::packaged_task如果你想包裹起来的东西,以便将它们移动到其他线程或以后打电话给他们。或者,引用克里斯蒂安

最后,a std::packaged_task只是一个用于实现的较低级别的功能std::async(这就是为什么它可以比std::async与其他较低级别的东西(如std::thread)一起使用还能做更多的事情的原因。简单地说,a std::packaged_taskstd::function与a的链接,std::futurestd::async包装和调用a std::packaged_task(可能在其他线程中)。


9
您应该补充一点,异步破坏所返回的未来会受到破坏(就像您调用了get一样),而从packaged_task返回的未来则不会。
John5342

22
最后,a std::packaged_task只是一个用于实现的较低级别的功能std::async(这就是为什么它可以比std::async与其他较低级别的东西(如std::thread)一起使用能做得更多的原因。简单地说,a std::packaged_taskstd::function与a的链接,std::futurestd::async包装和调用a std::packaged_task(可能在其他线程中)。
Christian Rau

我正在〜future()块上做一些实验。我无法复制对将来对象销毁的阻止效果。一切都异步工作。我正在使用VS 2013,当我启动异步时,我使用了std :: launch :: async。VC ++是否以某种方式“修复”了这个问题?
弗兰克·刘

1
@FrankLiu:好吧,N3451是一个被接受的提议,据我所知,它已经进入了C ++ 14。鉴于Herb在Microsoft工作,如果在VS2013中实现该功能,我不会感到惊讶。严格遵循C ++ 11规则的编译器仍会显示此行为。
Zeta 2014年

1
@Mikhail这个答案在C ++ 14和C ++ 17之前,所以我没有标准,只有手头的建议。我将删除该段。
Zeta

1

打包任务与异步

p>打包的任务包含任务[function or function object]和将来/承诺对。执行任务时return语句,它会导致set_value(..)packaged_task所做的承诺。

a>在给定Future,promise和package任务的情况下,我们可以创建简单的任务,而不必太担心线程[线程只是我们为运行任务而提供的东西]。

但是我们需要考虑要使用多少个线程,或者最好在当前线程还是另一个线程上运行一个任务async(),这种决定可以由名为的线程启动器处理,该启动器决定创建新线程还是回收旧线程。一个或仅在当前线程上运行任务。它返回了未来。


0

“类模板std :: packaged_task封装了任何可调用的目标(函数,lambda表达式,绑定表达式或另一个函数对象),以便可以异步调用它。它的返回值或抛出的异常以共享状态存储,可以访问通过std :: future对象。”

“模板函数async异步运行该函数f(可能在单独的线程中运行),并返回一个std :: future,它将最终保存该函数调用的结果。”

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.