Std::async保证在return语句之后运行

std::async guarantee to run after return statement

本文关键字:语句 之后 运行 return async Std      更新时间:2023-10-16

有哪些方法可以异步执行一个函数,但要保证它将在从当前函数返回后执行?

的例子:

   std::future<void> future;
   int do_smth( std::function<void()> callback) 
   { 
       int id = 0;
       auto cb = [=](){ callback(id);};
       future = std::async(std::launch::async, cb);
       return id;
   }

std::async调用应该在返回完成后执行。虽然不能保证会发生,但执行仍然可能在函数返回之前发生。

[Edit]我想要这个,这样我就可以实现观察者模式,像这样:

class A
{
public:
    void send_request()
    {
        m_id = do_smth( std::bind(&A::on_result,this,_1);
    }
   void on_result(RequestId id) 
   {
      if (id == m_id) {
          // my request finished!
      }
   }
private:
   RequestId m_id;
};

我知道我可以使用一个线程,一个队列,并发布事件,但我认为可能有一些更简单的东西,如果我可以使用std::async

您的示例将阻塞在std::async调用并等待异步任务完成(因此它是完全同步的)。

为了允许任务继续运行,你需要将返回的future存储在一个比函数更长寿的变量中,例如从函数中返回它,或者将它分配给函数外的一些长寿对象:

std::future<void> some_global_future;
int do_smth( std::function<void()> callback) 
{ 
  int id = 0;
  some_global_future = std::async(std::launch::async, callback, id);
  return id;
}

注意,不需要像示例中那样创建lambda。您可以将回调及其参数直接传递给std::async,它们将被复制并传递给新线程,相当于用于lambda的[=]捕获。

由于您似乎没有使用std::async返回的future,另一个选项是使用分离线程:

int do_smth( std::function<void()> callback) 
{ 
  int id = 0;
  std::thread(callback, id).detach();
  return id;
}

Update:防止新线程运行直到调用者退出,你需要一些同步,例如

std::mutex mx;
int do_smth( std::function<void()> callback) 
{ 
  int id = 0;
  std::lock_guard<std::mutex> lock(mx);
  auto cb = [=]{ std::lock_guard<std::mutex> l(mx); callback(id); };
  std::thread(cb).detach();
  return id;
}

这确保了callback(id)在互斥锁被锁定之前不会发生,这意味着do_smth必须已经返回。

然而,并不意味着对m_id的赋值已经完成:

    m_id = do_smth( std::bind(&A::on_result,this,_1);

操作系统调度程序可以运行do_smth,然后当它返回时解锁互斥锁,运行异步线程,然后当它完成后继续运行原始线程并分配给m_id

你将有一个竞争条件,因为一个线程分配给m_id,而运行回调的线程读取它。你需要在do_smth里面设置m_id来解决这个问题。