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的文章async
和async
,它描述了这个问题,而Scott Meyer的std::async
的std::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::future
和std::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一个新的线程还是回收一个旧的或简单地运行当前线程上的任务。 它返回未来。