首页 > 解决方案 > 如何在 C++ 中使用协程实现 async_sleep?

问题描述

我试图了解 C++ 协程是如何工作的,所以我尝试实现一个async_sleep不会阻塞的等待器:

Interleaver mycoro(std::function<void()> f1, std::function<void()> f2)
{
    f1();
    co_await async_sleep{2s};
    f2();
}

这是完整的代码:

#include <iostream>
#include <coroutine>
#include <chrono>
#include <functional>

using namespace std;


struct Interleaver {
    struct promise_type {
        Interleaver get_return_object() { return {coroutine_handle<promise_type>::from_promise(*this)}; }
        suspend_never initial_suspend() { return {}; }
        suspend_never final_suspend() noexcept { return {}; }
        void unhandled_exception() { exc_ = current_exception(); }
        void return_void() { }
        exception_ptr exc_;
    };
    operator bool() { return !handle_.done(); }
    void operator()() { handle_.resume(); }
    coroutine_handle<promise_type> handle_;
};


struct async_sleep {
    async_sleep(chrono::seconds dur) : dur_{dur} { }
    bool await_ready()
    {
        if (dur_ == 0s) return true;
        startTime_ = chrono::steady_clock::now();
        return false;
    }
    bool await_suspend(coroutine_handle<Interleaver::promise_type> h)
    {
        auto now = chrono::steady_clock::now();
        auto diff = now - startTime_;
        auto shouldSuspend = diff < dur_;
        return shouldSuspend;
    }
    void await_resume() { }

    chrono::seconds dur_;
    chrono::steady_clock::time_point startTime_;
};

Interleaver mycoro(std::function<void()> f1, std::function<void()> f2)
{
    f1();
    co_await async_sleep{2s};
    f2();
}

int main()
{
    auto i = mycoro([] { cout << "before" << endl; },
                [] { cout << "after" << endl; });
    while (i) {
        i();
    } 
}

问题是,一旦main()函数通过调用触发协程恢复i(),我就无法停止协程,它会在 2 秒过去之前恢复。我想要的行为是,直到 2 秒过去,协程才恢复(即使通过协程句柄恢复协程。)

我知道我可以在Interleaver类中实现一个 API,该 API 可以验证指定的时间是否已经过去,但我不希望协程的调用者具有恢复协程以外的任何能力。

我怎样才能做到这一点?

标签: c++c++20coroutine

解决方案


推荐阅读