47

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).

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
Violet Giraffe
  • 32,368
  • 48
  • 194
  • 335
  • So what do you expect to happen if the thread you throw off is in the middle of something, then `main` finishes? – Yakk - Adam Nevraumont May 04 '14 at 16:47
  • 2
    @Yakk: Don't care. It should be terminated along with whatever it was doing, which is perfectly fine in my application. – Violet Giraffe May 04 '14 at 17:04
  • 1
    So, you are ok with it formatting your hard drive because `main` exited first? Seems unwise to me! I prefer to avoid nasal demons myself. Undefined behavior is better avoided than ignored... The C++ standard is vague about what happens to detached threads at the end of `main`, to an extent I personally am not comfortable with. If you are targeting a single platform, you can have more confidence. – Yakk - Adam Nevraumont May 04 '14 at 17:12
  • 7
    @Yakk: you're demonizing UB beyond the point of common sense. – Violet Giraffe May 04 '14 at 17:25
  • 1
    Suppose the thread has aquired a remote resource on a server or on hardware on this system, that it will release via RAII. Your 'I do not care' will probably in practice leak that. If unlucky, the standard mandates *no* guarantees about what happens: if you are programming for one architecture and know the code will not be used for any other purpose (throw away code), you could detemine how they actually handle it, and get at worst the above result. `*(*char)0=7` is also a way to terminate that is usually safe. – Yakk - Adam Nevraumont May 04 '14 at 18:16
  • 2
    @Yakk: I'm not writing a library. I'm writing an application and know exactly what will be executed in this thread. So I know it's safe to either terminate or abandon it. Generally speaking - yes, of course you're right. This would be sloppy programming. – Violet Giraffe May 04 '14 at 19:15

2 Answers2

52

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

Community
  • 1
  • 1
merlin2011
  • 71,677
  • 44
  • 195
  • 329
  • 1
    Neat! I've missed the `detach` method. Thanks. – Violet Giraffe May 04 '14 at 09:56
  • 19
    Lambda is redundant here. `std::thread(run_async_task).detach();` – W.B. May 04 '14 at 10:04
  • Although the answer is correct, I'm pretty sure this is *not* the canonical C++11 way of doing things, but std::async is exactly meant for that! – Janick Bernet May 04 '14 at 10:09
  • 2
    @W.B.: indeed. Lambda is there because I need it for my actual code (I;m launching a void function with some parameters, and capture these parameters from the arguments of my `launch_task` function). – Violet Giraffe May 04 '14 at 10:12
  • 1
    Maybe would be good to add to the answer why std::async doesn't work, since to me its totally unexpected that the future destructor is blocking. – Janick Bernet May 04 '14 at 10:14
  • 8
    @JanickBernet with `std::async` you can't just launch and forget about it, because once the `std::future` return value goes out of scope it will block until the async function returns. – W.B. May 04 '14 at 10:17
  • 6
    @JanickBernet Unfortunately `std::async` is not quite "async". It is actually blocking call. – SwiftMango May 04 '14 at 10:25
  • 3
    @JanickBernet, have a look [here](http://en.cppreference.com/w/cpp/thread/async). _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_ – W.B. May 04 '14 at 10:25
  • 1
    @W.B. I pulled your comment into the answe.r – merlin2011 May 04 '14 at 18:37
  • 3
    If anyone else is interested in the reason why a future created from async blocks on destruction: http://stackoverflow.com/a/23460094/369310 – Janick Bernet May 04 '14 at 21:40
  • 1
    @VioletGiraffe the lambda is still redundant, [`std::thread::thread`](https://en.cppreference.com/w/cpp/thread/thread/thread) forwards arguments. – Caleth Jul 01 '19 at 12:28
27

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.

Miljen Mikic
  • 14,765
  • 8
  • 58
  • 66
  • 1
    But doesn't it do the same as `std::thread{...}.detach()` at the cost of a couple extra objects, extra memory allocation (for the pointer) and extra CPU cycles to increment/decrement the counter? – Violet Giraffe Jul 01 '19 at 17:13
  • 6
    @violetgiraffe It does the same thing as std::thread{..}.detach() but I have read at many places that it is not possible to achieve the same goal with std::async, which this answer disproved. Regarding the performance, Kurt Guntheroth found that creating a thread is 14 times more expensive than using an async (see https://www.amazon.com/Optimized-Proven-Techniques-Heightened-Performance/dp/1491922060/). Therefore, the cost of few extra CPU cycles and extra memory allocation could be negligible compared to that (one should measure, though). – Miljen Mikic Jul 01 '19 at 20:38
  • 4
    This technique defers the problem of thread management to the OS, which likely will do a better job than your application will. For example, if the implementation of `std::async` uses Grand Central Dispatch or some other thread management system under the hood, you leverage those capabilities here for free, instead of spinning up `N` threads for your `N` async tasks, and risk over-subscribing the machine. – fbrereto May 13 '20 at 21:48
  • This is really an excellent trick. Excellent as well to explain behaviour of lambda capture and shared_ptr ! Before finding this answer my first implementation was to store the future in some structure but I was struggling on how to remove it when the future was over... thanks a lot – sandwood Feb 26 '21 at 19:35
  • valgrind --leak-check=yes indicated me that there is a definitely lost block which is the futptr, has anyone else had the same memory leak? – armand nissim bendanan May 01 '22 at 08:17
  • @armandnissimbendanan sorry for the late response, I wasn't active at Stack Overflow lately. Valgrind is correct, this approach has a problem, I've edited the post and described the core issue. – Miljen Mikic Mar 04 '23 at 19:01