恢复 ASIO Stackless 协程
Resume ASIO Stackless Coroutine
在 Clang 中使用了 Coroutine TS 的当前实现后,我偶然发现了 asio stackless 协程实现。他们被描述为 Portable Stackless Coroutines in 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("%d\n", result);
}
std::thread([](){
std::this_thread::sleep_for(2s);
}).join();
return 0;
}
感谢您的帮助
首先,stackless coroutines 被更好地描述为可恢复函数。您当前遇到的问题是使用 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("%d\n", 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 将 运行 你放在它后面的表达式,应该是一个异步调用,否则会得到很少的好处
- 运行ning后,会跳出重入块
现在"start"完成了,你开始另一个线程等待。同时,foo 的线程结束睡眠并再次调用您的任务。现在:
- 重新进入块将读取协程的状态,发现它必须跳过 foo 调用
- 您的任务将恢复,打印结果并退出函数,返回到 foo 线程。
foo 线程现已完成,main 可能仍在等待第二个线程。
在 Clang 中使用了 Coroutine TS 的当前实现后,我偶然发现了 asio stackless 协程实现。他们被描述为 Portable Stackless Coroutines in 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("%d\n", result);
}
std::thread([](){
std::this_thread::sleep_for(2s);
}).join();
return 0;
}
感谢您的帮助
首先,stackless coroutines 被更好地描述为可恢复函数。您当前遇到的问题是使用 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("%d\n", 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 将 运行 你放在它后面的表达式,应该是一个异步调用,否则会得到很少的好处
- 运行ning后,会跳出重入块
现在"start"完成了,你开始另一个线程等待。同时,foo 的线程结束睡眠并再次调用您的任务。现在:
- 重新进入块将读取协程的状态,发现它必须跳过 foo 调用
- 您的任务将恢复,打印结果并退出函数,返回到 foo 线程。
foo 线程现已完成,main 可能仍在等待第二个线程。