Reputation: 191
What are some ways to execute a function asynchronously, but to have the guarantee that it will execute after returning from current function?
Example:
std::future<void> future;
int do_smth( std::function<void()> callback)
{
int id = 0;
auto cb = [=](){ callback(id);};
future = std::async(std::launch::async, cb);
return id;
}
The std::async
call should execute after return was finished. ASFAIK that it's not guaranteed to happen, the execution might still happen before function returned.
[Edit] I want this so I can implement the observer pattern, like this:
class A
{
public:
void send_request()
{
m_id = do_smth( std::bind(&A::on_result,this,_1);
}
void on_result(RequestId id)
{
if (id == m_id) {
// my request finished!
}
}
private:
RequestId m_id;
};
I know I can use a thread, with a queue, and post events but was thinking there might something simpler, if I could use the std::async
.
Upvotes: 3
Views: 2075
Reputation: 171433
Your example will block at the std::async
call and wait for the async task to finish (so it is entirely synchronous).
To allow the task to continue running you need to store the returned future in a variable that outlives the function, e.g. by returning it from the function, or assigning it to some long-lived object outside the function:
std::future<void> some_global_future;
int do_smth( std::function<void()> callback)
{
int id = 0;
some_global_future = std::async(std::launch::async, callback, id);
return id;
}
Note there is no need to create a lambda as in your example. You can pass the callback and its argument directly to std::async
, and they will be copied and passed to the new thread, equivalent to the [=]
capture you used for the lambda.
Since you don't appear to use the future
returned by std::async
another option is to use a detached thread:
int do_smth( std::function<void()> callback)
{
int id = 0;
std::thread(callback, id).detach();
return id;
}
Update: to prevent the new thread running until the caller has exited you would need some synchronisation e.g.
std::mutex mx;
int do_smth( std::function<void()> callback)
{
int id = 0;
std::lock_guard<std::mutex> lock(mx);
auto cb = [=]{ std::lock_guard<std::mutex> l(mx); callback(id); };
std::thread(cb).detach();
return id;
}
This ensures that callback(id)
won't happen until the mutex can be locked, which means do_smth
must have returned.
However, that does not mean that the assignment to m_id
will have completed:
m_id = do_smth( std::bind(&A::on_result,this,_1);
The OS scheduler could run do_smth
then when it returns unlock the mutex, run the asynchronous thread, then when that finishes continue running the original thread and assign to m_id
.
You will have a race condition here due to one thread assigning to m_id
while the thread running the callback reads it. You need to set m_id
inside do_smth
to fix that.
Upvotes: 2