Violet Giraffe
Violet Giraffe

Reputation: 33579

What's the C++ 11 way to fire off an asynchronous task and forget about it?

I need something like this:

void launch_task()
{
    std::thread([](){ run_async_task(); });
}

Except thread's destructor will terminate my task. I don't need any control over the task, don't need a return value either. It just has to run its course and then the thread should terminate and C++ thread object should be disposed of. What C++ 11 facility do I need?

I've looked at std::async, but couldn't find an example of usage for my case. It seems to be a pretty complicated system, and I'd need to somehow store and manipulate std::future or it'd become synchronous (if my understanding is correct; I didn't find a good clear article on std::async).

Upvotes: 48

Views: 17264

Answers (2)

Miljen Mikic
Miljen Mikic

Reputation: 15241

Resurrecting an old thread, but there is a neat trick* on how to achieve "fire and forget" functionality by using std::async as well, despite the blocking std::future that it returns. The main ingredient is a shared pointer to returned std::future that is captured in lambda by value, causing its reference counter to be incremented. This way the destructor of the std::future won't be invoked until lambda finished its work, providing real asynchronous behaviour, as desired.

template <class F>
void call_async(F&& fun) {
    auto futptr = std::make_shared<std::future<void>>();
    *futptr = std::async(std::launch::async, [futptr, fun]() {
        fun();
    });
}

*Kudos to a colleague of mine and true C++ expert, MVV, who showed me this trick.

EDIT 2023-03-04

Do not use the trick shown above in the production code, unfortunately, it has a problem. It's pretty much similar to the following snippet:

...
*futptr = std::async(std::launch::async, [futptr, self = shared_from_this()]() mutable {
    fprintf(stderr, "std::async done.\n");
    futptr.reset();
    fprintf(stderr, "std::async really done.\n");
});
...

where std::async really done. will never be printed. In short, futptr.reset() forces std::future destructor and this one waits for a std::promise to be fulfilled which can't happen because the function is still in progress. The same happens when we call reset(), in that case again we have an implicit reset() - this time in futptr's destructor.

However, there's still a way to achieve "fire and forget" without using std::thread and detach(): ASIO and its post function.

Upvotes: 26

merlin2011
merlin2011

Reputation: 75565

Just detach it immediately after creation.

std::thread([](){ run_async_task(); }).detach();

Once detached, the thread will no longer be joinable, so ~thread() will have no effect. This answer discusses more details of this behavior.

As mentioned by W.B. below, std::async will not work for the following reason, pulled from this reference.

If the std::future obtained from std::async has temporary object lifetime (not moved or bound to a variable), the destructor of the std::future will block at the end of the full expression until the asynchronous operation completes

Upvotes: 54

Related Questions