packaged_task和async之间有什么区别?

在使用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::launch::deferred运行std::async ,但是在这种情况下有一个主要区别吗?

这两种方法有什么区别,更重要的是,我应该在哪些用例上使用它们呢?

其实你刚刚给的例子显示了如果你使用一个相当长的函数,如

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

打包的任务

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

另一方面, launch::async std::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"; 

缺点(仅限C ++ 11)

但是在你尝试使用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; */ 

所以,如果你想要真正的asynchronous,你需要保留返回的future ,或者如果情况改变,你不关心结果:

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

有关这方面的更多信息,请参阅Herb Sutter的文章asyncasync ,它描述了这个问题,而Scott Meyer的std::asyncstd::futures并不特别 ,它描述了这些见解。 还要注意这个行为是特定于C ++ 11的 ,在C ++ 14中这个析构函数不会在调用线程中阻塞 。

更多的差异

通过使用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"; 

另外,在调用f.get()之前需要调用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 。 或者,引用基督教 :

最后, std::packaged_task只是实现std::async一个较低级别的function(这就是为什么它可以做更多比std::async如果与其他更低级别的东西,如std::thread一起使用)。 简单地说,一个std::packaged_task是一个std::function链接到一个std::futurestd::async包装并调用一个std::packaged_task (可能在不同的线程中)。

“类模板std :: packaged_task封装了任何可调用的目标(函数,lambdaexpression式,绑定expression式或其他函数对象),以便它可以asynchronous调用。其返回值或抛出的exception存储在可以访问的共享状态通过std :: future对象“。

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

打包任务vsasynchronous

打包任务包含一个任务[function or function object]和未来/保证对。 当任务执行返回语句时,它会在packaged_task的承诺上产生set_value(..)

一个>给定的未来,承诺和包任务,我们可以创build简单的任务,而不必担心太多的线程[线程只是我们给的任务]。

然而,我们需要考虑有多less线程可以使用,或者一个任务是最好在当前线程还是另一个线程上运行等等。这些决策可以通过一个称为async()的线程启动器来处理,决定是创build一个新的线程还是回收一个旧的或简单地运行当前线程上的任务。 它返回未来。

Interesting Posts