Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 301
Germán Diago Avatar asked Aug 25 '14 07:08

Germán Diago


2 Answers

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...

like image 123
Nim Avatar answered Nov 16 '22 06:11

Nim


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

and

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

    return shared_ftr;
}

are equivalent. The later will work exactly when the former will.

The future in the function that goes out of scope is moved from and therefore can't block. The blocking most likely happens in the calling function, which you have not shown.

like image 24
Jan Hudec Avatar answered Nov 16 '22 06:11

Jan Hudec