Будет ли этот асинхронный трюк работать, или состояние будет болтаться, когда я его получу?

Я столкнулся с ситуацией, когда было бы неплохо запустить операцию std::async полностью асинхронно.

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

Проблема в том, что функция будет блокироваться в конце, если я не сохраню будущее. Я бы хотел, чтобы этого не произошло.

Это помешало бы std::future вызвать его деструктор в конце области действия:

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

Может ли это работать? Что происходит, когда я не сохраняю результат в переменной?

Ответ 1

Вот полноценный пример. Этот шаблон работает, я использую его широко с помощью asio и асинхронных операций.

#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));
}

Единственное беспокойство, которое я хотел бы выразить, заключается в том, что ждать в конце, не захватывая future (независимо от того, движется ли он или через shared_ptr), у вас нет возможности остановить приложение от завершения до завершения задачи..

Если у вас есть другой способ обеспечения продолжения, то подход shared_ptr будет работать нормально. Иначе, пойдите с перенесенным будущим, это чище...

Ответ 2

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

и

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

    return shared_ftr;
}

эквивалент. Позднее будет работать точно, когда будет первый.

Будущее в функции, выходящей из области видимости, перемещается из и, следовательно, не может блокироваться. Блокировка, скорее всего, происходит в вызывающей функции, которую вы не указали.