恢复ASIO无堆栈Coroutine

Resume ASIO Stackless Coroutine

本文关键字:Coroutine 堆栈 ASIO 恢复      更新时间:2023-10-16

在Clang中玩了一点Coroutine TS的当前实现后,我偶然发现了无asio堆栈的协同程序实现。它们被描述为One*Header中的可移植无堆栈子程序。主要处理异步代码时,我也想尝试一下。

main函数内的协同程序块应等待函数foo中派生的线程异步设置的结果。然而,我不确定一旦线程设置了值,如何在点<1>(在yield表达式之后(继续执行。

使用Coroutine TS,我会调用coroutine_handle,但是boost::asio::coroutine似乎不可调用。

使用boost::asio::coroutine是否可以实现这一点?

#include <thread>
#include <chrono>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/yield.hpp>
#include <cstdio>
using namespace std::chrono_literals;
using coroutine = boost::asio::coroutine;
void foo(coroutine & coro, int & result) {
std::thread([&](){
std::this_thread::sleep_for(1s);
result = 3;
// how to resume at <1>?
}).detach();
}
int main(int, const char**) {
coroutine coro;
int result;
reenter(coro) {
// Wait for result
yield foo(coro, result);
// <1>
std::printf("%dn", result);
}
std::thread([](){
std::this_thread::sleep_for(2s);
}).join();
return 0;
}

感谢您的帮助

首先,无堆栈协程被更好地描述为可恢复的函数。您当前遇到的问题是使用main。如果你把你的逻辑提取到一个单独的函子中,这是可能的:

class task; // Forward declare both because they should know about each other
void foo(task &task, int &result);
// Common practice is to subclass coro
class task : coroutine {
// All reused variables should not be local or they will be
// re-initialized
int result;
void start() {
// In order to actually begin, we need to "invoke ourselves"
(*this)();
}
// Actual task implementation
void operator()() {
// Reenter actually manages the jumps defined by yield
// If it's executed for the first time, it will just run from the start
// If it reenters (aka, yield has caused it to stop and we re-execute)
// it will jump to the right place for you
reenter(this) {
// Yield will store the current location, when reenter
// is ran a second time, it will jump past yield for you
yield foo(*this, result);
std::printf("%dn", result)
}
}
}
// Our longer task
void foo(task & t, int & result) {
std::thread([&](){
std::this_thread::sleep_for(1s);
result = 3;
// The result is done, reenter the task which will go to just after yield
// Keep in mind this will now run on the current thread
t();
}).detach();
}
int main(int, const char**) {
task t;
// This will start the task
t.start();
std::thread([](){
std::this_thread::sleep_for(2s);
}).join();
return 0;
}

请注意,不可能从子函数中屈服。这是无堆栈协程的一个限制。

工作原理:

  • yield存储一个唯一的标识符,以便跳转到协程内部
  • yield将运行您放在它后面的表达式,应该是异步调用,否则将不会带来什么好处
  • 运行后,它将突破可重入块

现在"启动"完成,您可以启动另一个线程等待。同时,foo的线程完成睡眠并再次调用您的任务。现在:

  • 重入块将读取协程的状态,以发现它必须跳过foo调用
  • 您的任务将继续,打印结果并退出函数,返回到foo线程

foo线程现在已经完成,main可能仍在等待第二个线程。