【问题标题】:Resume ASIO Stackless Coroutine恢复 ASIO 无堆栈协程
【发布时间】:2019-01-14 03:49:30
【问题描述】:

在 Clang 中玩了一点 Coroutine TS 的当前实现后,我偶然发现了 asio 无堆栈协程实现。他们被描述为Portable Stackless Coroutines in One* Header。 主要处理异步代码,我也想尝试一下。

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

使用协程 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;
}

感谢您的帮助

【问题讨论】:

  • 我不认为 asio 协程旨在跨线程移动控制流。

标签: c++ coroutine stackless


【解决方案1】:

首先,无堆栈协程更好地描述为可恢复函数。您当前遇到的问题是使用 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 将运行您放在它后面的表达式,应该是异步调用,否则几乎没有什么好处
  • 运行后会跳出reenter block。

现在“开始”已经完成,你开始另一个线程等待。同时,foo 的线程完成休眠并再次调用您的任务。现在:

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

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

【讨论】:

    猜你喜欢
    • 2015-05-12
    • 2021-06-10
    • 2016-12-21
    • 2015-05-24
    • 2017-04-22
    • 2012-05-20
    • 2014-12-28
    • 2019-12-01
    • 1970-01-01
    相关资源
    最近更新 更多