如果我打电话
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 返回的任务放入变量中并将其存储直到任务完成,但这没有帮助,并且查看代码,看起来它没有努力将函子保留在范围内,直到它完成为止。完成了。
这只是顶层的问题,因为嵌套任务是等待的,所以它们的函子在完成之前不会超出范围。
但是如何启动顶级协程并将仿函数保持在作用域内?
来自 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);
}