Reputation: 93264
I want a member std::future<void>
to continuously call a function inside a loop until the parent object is destroyed.
My current solution involves wrapping the future in a class with a boolean flag and setting the flag to false on destruction.
class Wrapper
{
std::future<void> fut;
bool wrapperAlive{true};
public:
Wrapper() : fut{std::async(std::launch::async, [this]
{
while(wrapperAlive) doSomething();
})} { }
~Wrapper()
{
wrapperAlive = false;
}
};
Is there a more idiomatic way of doing this?
Upvotes: 1
Views: 1867
Reputation: 275310
This is a data-race free version of your code:
class Wrapper {
std::atomic<bool> wrapperAlive{true}; // construct flag first!
std::future<void> fut;
public:
Wrapper() :
fut{std::async(std::launch::async, [this]
{
while(wrapperAlive)
doSomething();
}
)}
{}
~Wrapper() {
wrapperAlive = false;
fut.get(); // block, so it sees wrapperAlive before it is destroyed.
}
};
the next thing I'd do is write:
template<class F>
struct repeat_async_t {
F f;
// ...
};
using repeat_async = repeat_async_t<std::function<void()>>;
template<class F>
repeat_async_t<std::decay_t<F>> make_repeat_async(F&&f){
return {std::forward<F>(f)};
}
which takes a task to repeat forever, and bundle it up in there, rather than mixing the flow logic with what is executed logic.
At this point, we will probably want to add in an abort method.
Finally, it is very rarely a good idea to busy-loop a thread. So we'd add in some kind of wait-for-more-data-to-consume system.
And it ends up looking a lot different than your code.
Upvotes: 3