Some time ago I was looking for a way to invoke std::async
without the need of storing std::future
, thus not blocking the execution at the end of the scope. I found this answer which uses a captured std::shared_ptr
for an std::future
, therefore allowing to make a nonblocking call to std::async
.
Another way of deferring a destructor invocation is to prevent it from to be called at all. This can be achieved with in-place construction with operator new
.
Consider this version that uses a static thread local storage for an in-place constructed std::future<void>
:
template <class F>
void call_async(F&& fun) {
thread_local uint8_t buf[sizeof(std::future<void>)] = {0};
auto fut = new(buf) std::future<void>();
*fut = std::async(std::launch::async, [fun]() {
fun();
});
}
This version will not produce any heap-allocation related overhead, but it seems very illegal, though I am not sure why in particular.
I am aware that it is UB to use an object before it has been constructed, which is not the case. I am not sure why not calling delete
in this case would resolve in UB (for heap allocation it is not UB).
Possible problems that I see:
std::promise
I suppose)https://ideone.com/C44cfe
UPDATE
Constructing an object in the static storage directly (as has mentioned IlCapitano in the comments) will block each time a move assignment is called (shared state will be destroyed blocking the thread which has removed last reference to it).
Not calling a destructor will case a leak because of not released references to the shared state.
So if you want to make sure that the work is done asynchronously, use std::launch::async . @user2485710 it needs to block when you retrieve the result, if you need the result in the launching thread. It cannot use the result if the result is not ready. So if you go to get the result, you have to wait until it is ready.
If the std::async runs on an other thread, the async call will not return until printThreadId is done working. That is, std::async will block.
std::async. Calls fn (with args as arguments) at some point, returning without waiting for the execution of fn to complete. The value returned by fn can be accessed through the future object returned (by calling its member future::get ).
How does std::launch::async Work in Different Implementations? For now, we know that if no policy is specified, then std::async launches a callable function in a separate thread. However, the C++ standard does not specify whether the thread is a new one or reused from a thread pool.
Blocking an async call can lead to the same negative impact, but it won’t deadlock the first thread. As a result, you should not block an async call until it is actually needed. When blocking a call to an async method, you transform the asynchronous code into synchronous code.
Another problem with async calls is that they are often very difficult to read. The first problem with blocking async calls is that it turns asynchronous code into synchronous code. This can lead to unexpected blocking of context threads and deadlocks.
Moreover, an async call would require the use of a thread pool, a scheduler, and a complete synchronous programming system. While async calls are useful for entertainment and preventing mobile applications from freezing, they’re not very efficient. They use more resources than normal application usage, causing the app to slow down.
The new runtime relieves you of these concerns and allows you to do the blocking operation directly inside the async function: The runtime measures the time it takes to perform the blocking operation and if it takes a while, a new thread is automatically spawned and replaces the old executor thread.
It's undefined behaviour to end the lifetime of a non-trivial object without calling it's destructor, which happens as soon as there is a second call_async
invocation.
"heap-allocation related overhead" is a misnomer if the only alternative is undefined behaviour. The future
returned by async
has to live somewhere.
The updated code has defined behaviour: it waits for the previous invocation to be done before launching the next one.
Calling std::async
and ignoring the result sounds like "fire and forget". The simplest way to do that is to not use std::async
, but to create a detached thread:
std::thread thr(func, data...);
thr.detach();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With