PPL 并发和 co_await 导致 lambda 捕获变量在挂起时超出范围

问题描述 投票:0回答:1

如果我打电话

concurrency::create_task([self,this]() -> concurrency::task<void>
{ 
   co_await 5s; //or really any task that suspends until complete
   MemberFunction();
   co_return;
});

在挂起点之后,当我调用 MemberFunction() 时,this 指针为 NULL,并且 self 上的析构函数已被调用 - 因为虽然代码段和本地函数堆栈已恢复,但最初用作的实际 lambda 对象函子超出了范围。

ASIO 捕获函子并将其保留在范围内,直到执行完毕,但 PPL 并发运行时似乎不会。

如何在 co_await 之后维护状态,而不是在挂起之前将所有捕获变量复制到本地函数堆栈上(这确实有效,但这是可维护性的噩梦)?

我尝试将 create_task 返回的任务放入变量中并将其存储直到任务完成,但这没有帮助,并且查看代码,看起来它没有努力将函子保留在范围内,直到它完成为止。完成了。

这只是顶层的问题,因为嵌套任务是等待的,所以它们的函子在完成之前不会超出范围。

但是如何启动顶级协程并将仿函数保持在作用域内?

c++20 ppl
1个回答
0
投票

来自 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines

“对于普通 lambda 正确的使用模式对于协程 lambda 来说是危险的。捕获变量的明显模式将导致在第一个挂起点之后访问已释放的内存,即使对于重新计数的智能指针和可复制类型也是如此。”

坏:

int value = get_value();
std::shared_ptr<Foo> sharedFoo = get_foo();
{
  const auto lambda = [value, sharedFoo]() -> std::future<void>
  {
    co_await something();
    // "sharedFoo" and "value" have already been destroyed
    // the "shared" pointer didn't accomplish anything
  };
  lambda();
} // the lambda closure object has now gone out of scope

好:

int value = get_value();
std::shared_ptr<Foo> sharedFoo = get_foo();
{
  // take as by-value parameter instead of as a capture
  const auto lambda = [](auto sharedFoo, auto value) -> std::future<void>
  {
    co_await something();
    // sharedFoo and value are still valid at this point
  };
  lambda(sharedFoo, value);
} // the lambda closure object has now gone out of scope

最佳:

std::future<void> Class::do_something(int value, std::shared_ptr<Foo> sharedFoo)
{
  co_await something();
  // sharedFoo and value are still valid at this point
}

void SomeOtherFunction()
{
  int value = get_value();
  std::shared_ptr<Foo> sharedFoo = get_foo();
  do_something(value, sharedFoo);
}
© www.soinside.com 2019 - 2024. All rights reserved.