与其将未来移动到全局对象中(并手动管理未使用的未来的删除),您实际上可以将其移动到异步调用函数的本地范围内。
可以这么说,“让异步函数拥有自己的未来”。
我想出了这个适用于我的模板包装器(在 Windows 上测试):
#include <future>
template<class Function, class... Args>
void async_wrapper(Function&& f, Args&&... args, std::future<void>& future,
std::future<void>&& is_valid, std::promise<void>&& is_moved) {
is_valid.wait(); // Wait until the return value of std::async is written to "future"
auto our_future = std::move(future); // Move "future" to a local variable
is_moved.set_value(); // Only now we can leave void_async in the main thread
// This is also used by std::async so that member function pointers work transparently
auto functor = std::bind(f, std::forward<Args>(args)...);
functor();
}
template<class Function, class... Args> // This is what you call instead of std::async
void void_async(Function&& f, Args&&... args) {
std::future<void> future; // This is for std::async return value
// This is for our synchronization of moving "future" between threads
std::promise<void> valid;
std::promise<void> is_moved;
auto valid_future = valid.get_future();
auto moved_future = is_moved.get_future();
// Here we pass "future" as a reference, so that async_wrapper
// can later work with std::async's return value
future = std::async(
async_wrapper<Function, Args...>,
std::forward<Function>(f), std::forward<Args>(args)...,
std::ref(future), std::move(valid_future), std::move(is_moved)
);
valid.set_value(); // Unblock async_wrapper waiting for "future" to become valid
moved_future.wait(); // Wait for "future" to actually be moved
}
我有点惊讶它的工作原理,因为我认为移动的未来的析构函数会阻塞,直到我们离开async_wrapper。它应该等待async_wrapper返回,但它正在那个函数内部等待。从逻辑上讲,它应该是一个死锁,但事实并非如此。
我还尝试在async_wrapper末尾添加一行以手动清空未来对象:
our_future = std::future<void>();
这也不会阻塞。