Differencebetween Packaged_Task and Async

What is the difference between packaged_task and async

Actually the example you just gave shows the differences if you use a rather long function, such as

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

Packaged task

A packaged_task won't start on it's own, you have to invoke it:

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

On the other hand, std::async with launch::async will try to run the task in a different thread:

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";

Drawback

But before you try to use async for everything, keep in mind that the returned future has a special shared state, which demands that future::~future blocks:

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;
*/

So if you want real asynchronous you need to keep the returned future, or if you don't care for the result if the circumstances change:

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

For more information on this, see Herb Sutter's article async and ~future, which describes the problem, and Scott Meyer's std::futures from std::async aren't special, which describes the insights. Also do note that this behavior was specified in C++14 and up, but also commonly implemented in C++11.

Further differences

By using std::async you cannot run your task on a specific thread anymore, where std::packaged_task can be moved to other threads.

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";

Also, a packaged_task needs to be invoked before you call f.get(), otherwise you program will freeze as the future will never become ready:

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

TL;DR

Use std::async if you want some things done and don't really care when they're done, and std::packaged_task if you want to wrap up things in order to move them to other threads or call them later. Or, to quote Christian:

In the end a std::packaged_task is just a lower level feature for implementing std::async (which is why it can do more than std::async if used together with other lower level stuff, like std::thread). Simply spoken a std::packaged_task is a std::function linked to a std::future and std::async wraps and calls a std::packaged_task (possibly in a different thread).

Why std::future is different returned from std::packaged_task and std::async?

std::async has definite knowledge of how and where the task it is given is executed. That is its job: to execute the task. To do that, it has to actually put it somewhere. That somewhere could be a thread pool, a newly created thread, or in a place to be executed by whomever destroys the future.

Because async knows how the function will be executed, it has 100% of the information it needs to build a mechanism that can communicate when that potentially asynchronous execution has concluded, as well as to ensure that if you destroy the future, then whatever mechanism that's going to execute that function will eventually get around to actually executing it. After all, it knows what that mechanism is.

But packaged_task doesn't. All packaged_task does is store a callable object which can be called with the given arguments, create a promise with the type of the function's return value, and provide a means to both get a future and to execute the function that generates the value.

When and where the task actually gets executed is none of packaged_task's business. Without that knowledge, the synchronization needed to make future's destructor synchronize with the task simply can't be built.

Let's say you want to execute the task on a freshly-created thread. OK, so to synchronize its execution with the future's destruction, you'd need a mutex which the destructor will block on until the task thread finishes.

But what if you want to execute the task in the same thread as the caller of the future's destructor? Well, then you can't use a mutex to synchronize that since it all on the same thread. Instead, you need to make the destructor invoke the task. That's a completely different mechanism, and it is contingent on how you plan to execute.

Because packaged_task doesn't know how you intend to execute it, it cannot do any of that.

Note that this is not unique to packaged_task. All futures created from a user-created promise object will not have the special property of async's futures.

So the question really ought to be why async works this way, not why everyone else doesn't.

If you want to know that, it's because of two competing needs: async needed to be a high-level, brain-dead simple way to get asynchronous execution (for which sychronization-on-destruction makes sense), and nobody wanted to create a new future type that was identical to the existing one save for the behavior of its destructor. So they decided to overload how future works, complicating its implementation and usage.

Is std::packaged_taskT (Function Template) an std::async (FT) with an invoked function?

std::async(ploicy, callable, args...)

launches a new thread (if the resources are available) if the policy is std::async::launch.

If the policy is not determined, it may launch or not.

If the policy is std::async::deferred, won't launch.

while std::packaged_task wraps your callable so that it can be invoked asynchronously using a new thread like

auto t1 = std::thread(std::move(taskObj), args...);
....
t1.join();

But If you used it as you do in your example, it wouldn't launch a new thread. It doesn't launch a new thread by itself but it can be used to do that.

std::future still deferred when using std::packaged_task (VS11)

This is an issue with VS2012. It's not the only issue either --- their implementation of the C++11 thread library has several bugs. I wrote about a few on my blog.

How to implement async waiting loop with packaged_task in C++?

If you don't want to use futureResultOne.get(); to avoid blocking, the following is a way to check if your task has finished or not in the main (you can use an independent one of course) thread.

#include <iostream>
#include <future>
#include <thread>
#include <chrono>
#include <condition_variable>

using namespace std::chrono_literals;

std::condition_variable cv;
bool theThreadHasFinished;
std::mutex mut;
int main()
{
std::packaged_task<int()> packagedTaskOne([]{
std::this_thread::sleep_for(4s);
return 4;
});

std::future<int> futureResultOne = packagedTaskOne.get_future();
std::thread tOne([&]{packagedTaskOne();
std::lock_guard lg{mut};
theThreadHasFinished = true;
cv.notify_all();});
tOne.detach();
std::unique_lock<std::mutex> lock{ mut };
cv.wait(lock, [ ](){ return theThreadHasFinished; });
std::cout << "done\n";

return 0;
}

You can use a while loop ad wait_for but this consumes your resources



Related Topics



Leave a reply



Submit