调度分离的协同程序 有很多教程解释了C++中使用CORDOTION是多么容易,但是我花了很多时间来规划分离的协同程序。 假设,我对协同程序结果类型有以下定义: struct task { struct promise_type { auto initial_suspend() const noexcept { return std::suspend_never{}; } auto final_suspend() const noexcept { return std::suspend_never{}; } void return_void() const noexcept { } void unhandled_exception() const { std::terminate(); } task get_return_object() const noexcept { return {}; } }; };

调度分离的协同程序 有很多教程解释了C++中使用CORDOTION是多么容易,但是我花了很多时间来规划分离的协同程序。 假设,我对协同程序结果类型有以下定义: struct task { struct promise_type { auto initial_suspend() const noexcept { return std::suspend_never{}; } auto final_suspend() const noexcept { return std::suspend_never{}; } void return_void() const noexcept { } void unhandled_exception() const { std::terminate(); } task get_return_object() const noexcept { return {}; } }; };,c++,c++20,C++,C++20,还有一种方法可以运行分离的协程,即异步运行它 /// Handler should have overloaded operator() returning task. template<class Handler> void schedule_coroutine(Handler &&handler) { std::thread([handler = std::forward<Handler>(handler)]() { handler(); }).d

还有一种方法可以运行分离的协程,即异步运行它

/// Handler should have overloaded operator() returning task.
template<class Handler>
void schedule_coroutine(Handler &&handler) {
  std::thread([handler = std::forward<Handler>(handler)]() { handler(); }).detach();
}

我认为应该有一种方法以某种方式保存处理程序,最好是在协同路由承诺附近或之内,这样下次恢复协同路由时,它就不会试图访问被破坏的对象数据。但不幸的是,我不知道怎么做。

我希望我说的对,但我认为这里可能有一些误解。首先,您显然无法分离协同程序,这根本没有任何意义。但是你可以在一个协同程序中执行异步任务,即使在我看来这完全违背了它的目的

但是让我们看看你发布的第二个代码块。这里调用std::async并将处理程序转发给它。现在,为了防止任何类型的早期破坏,应该改用std::move,并将处理程序传递给lambda,这样只要lambda函数的作用域有效,它就会保持活动状态。这可能已经回答了您的最后一个问题,因为您希望存储此处理程序的位置将是lambda捕获本身

另一件困扰我的事情是std::async的使用。该调用将返回一个std::future类型,该类型将阻塞,直到lambda被执行为止。但是,只有将启动类型设置为std::launch::async时,才会发生这种情况,否则需要调用.get或.wait,因为默认启动类型为std::launch::deferred,这只会延迟触发,意思是:当您实际请求结果时


因此,在您的情况下,如果您真的希望以这种方式使用协同路由,我建议使用std::thread,并将其存储起来,以便以后在伪全局的某个地方进行连接。但是,我想你不会真的希望这样使用协同程序机制。

我认为你的问题是对协同等待协同程序如何工作的普遍误解

当函数执行co_wait时,这通常意味着函数将暂停执行,直到expr恢复执行。也就是说,函数正在等待某个进程完成并通常返回一个值。这个过程,由expr表示,通常是应该恢复功能的过程

其要点是使异步执行的代码尽可能看起来像同步代码。在同步代码中,您可以执行类似.wait的操作,其中wait是一个函数,它等待expr表示的任务完成。不是等待它,而是等待或异步等待它。函数的其余部分相对于调用方异步执行,具体取决于expr完成的时间以及它决定恢复函数执行的方式。在这种情况下,co_wait看起来和行为非常像。等等

然后,编译器Magictm进入后台使其异步

因此,在这个框架内,启动分离的协同程序的想法是没有意义的。协同路由函数的调用方通常不是决定协同路由执行位置的人;是协同程序在执行期间调用的进程决定了这一点

您的schedule_coroutine函数实际上应该只是一个常规的异步执行函数操作。它不应该与协程有任何特定的关联,也不应该期望给定的函子是或代表某个异步任务,或者如果它碰巧调用了co_wait。函数只是创建一个新线程并在其上执行一个函数

正如您在C++20之前所做的那样


如果您的任务类型表示异步任务,那么在适当的RAII样式中,其析构函数应该等到任务完成后再退出,这包括在所述任务的整个执行过程中,该任务所调度的协程的任何恢复。这项任务直到完全完成才完成。因此,若schedule_协同程序调用中的处理程序返回一个任务,那个么该任务将被初始化并立即销毁。由于析构函数等待异步任务完成,因此在任务完成之前,线程不会死亡。由于线程的函子是从给定给线程构造函数的函数对象复制/移动的,因此任何捕获都将继续存在,直到线程本身退出。

我忘记了std::async返回了在任何情况下都应该等待的未来线程,这不是我的意思。我唯一想做的就是在一个单独的线程中运行一个协同程序,只是生成并忘记它。。。至少现在是这样。那么恐怕我没有正确地理解你的例子。据我所知,根本就没有合作项目,所以很难理解你的想法
你想在这里取得成就。所以,当您想要异步生成一个协同路由时,也就是说在其他地方的任务中,我不明白为什么这不起作用。另一方面,您在所有权方面面临的问题清楚地表明,您的预期设计可能有点偏离,或者换句话说,当您想要分离某些内容时,您应该已经知道存储结果的全局位置,或者根本不分离它。更新的代码似乎存在错误放置处理程序的语法问题,仍然不清楚是什么阻止您保存处理程序。您可以通过正确的转发按值捕获它。也许您还应该检查@deedemgadoodoo:coroutine将在另一个线程中执行,但是Hander没有问题,因为该线程根本不是一个coroutine,只是一个函数调用将有一个它给定的函子的副本。如果处理程序调用本身是一个协程,那么您应该通过它的返回值与它通信。这就是你的lambda需要做的。@deedeemegadoo因为一旦协同程序被挂起,传递到std::thread方法中的lambda将被销毁,所有捕获的变量都不是真的。当您使用std::thread时,它的所有值都会移动/复制到新线程中,并且在该线程终止之前不会消失。据推测,任务析构函数将在任务完成之前停止,因此,在handle返回的任务被销毁之前,schedule_coroutine函数中的lambda不会退出。
task coroutine_1() {
  std::vector<object> objects;
  // ...
  schedule_coroutine([objects]() -> task {
     // ...
     co_await something;
     // ...
     co_return;
  });

  // ...
  co_return;
}

int main() {
  // ...
  schedule_coroutine(coroutine_1);
  // ...
}