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.