Search code examples
c++multithreadingc++11asynchronousdangling-pointer

Will this async trick work or the state will be dangling when I access it?


I am facing a situation where it would be nice to launch an std::async operation totally asynchronously.

future<void> MyClass::MyAsyncFunc() {
    std::future<void> f = std::async(...);
    return f;
}  // The future goes out of scope, will block.

The problem is that the function will block at the end if I don't save the future. I would like this not to happen.

This would prevent the std::future to call its destructor at the end of the function's scope:

shared_ptr<future<void>> MyClass::MyAsyncFunc() {
    auto shared_ftr = std::make_shared<std::future<void>>();
    *shared_ftr = std::async([shared_ftr]() {...});
    return shared_ftr;
}

Could this possibly work? What happens when I don't save the result in a variable?


Solution

  • Here is a fully fledged example. This pattern does work, I use it extensively with boost asio and asynchronous operations.

    #include <chrono>
    #include <iostream>
    #include <future>
    #include <memory>
    #include <thread>
    
    std::shared_ptr<std::future<int>> get_task()
    // std::future<int> get_task() // rely on move, future supports move
    {
      auto f = std::make_shared<std::future<int>>();
      //std::future<int> f = std::async(std::launch::async, [] {
      *f = std::async(std::launch::async, [f] {
        (void) f;
        std::cout << "calculating" << std::endl;
        for (int x = 0; x < 10; ++x)
          std::this_thread::sleep_for( std::chrono::milliseconds( 200 ) );
        std::cout << "done." << std::endl;
        return 100;
      });
    
      return f;
    }
    
    
    int main(void)
    {
      std::cout << "getting task" << std::endl;
      //auto f = get_task(); <-- the future is moved not copied, so there is no block here
      get_task();
      std::cout << "waiting" << std::endl;
    //  f.wait(); <-- now wait for it to complete...
    //  std::cout << " got: " << f.get() << std::endl;
      // Wait for the truly async task to complete...
      std::this_thread::sleep_for(std::chrono::milliseconds(3000));
    }
    

    The only concern I'd express is that wait at the end, without capturing the future (whether it's moved or via shared_ptr), you have no way to stop the app from terminating before the task completes...

    If you have some other way of ensuring continuation, then the shared_ptr approach will work fine. Else, go with the moved future, it's cleaner...