C++协程TS(2017)可等待对象示例实现技术问询
co_await for std::chrono::duration in C++ Coroutines TS (2017) Alright, let's break down this code step by step—what it does, how the coroutine mechanics work here, and address common questions you might have.
First, here's the full code snippet we're analyzing (with the omitted await_suspend logic filled in for context):
template <class Rep, class Period> auto operator co_await(std::chrono::duration<Rep, Period> d) { struct awaiter { std::chrono::system_clock::duration duration; awaiter(std::chrono::system_clock::duration d) : duration(d){} bool await_ready() const { return duration.count() <= 0; } void await_resume() {} void await_suspend(std::experimental::coroutine_handle<> h){ // Typical implementation: Use a timer to resume the coroutine later std::jthread([h, this]() { std::this_thread::sleep_for(duration); h.resume(); }).detach(); } }; return awaiter{d}; }
What This Code Does
This is an overload of the operator co_await for any std::chrono::duration type (like std::chrono::seconds, std::chrono::milliseconds, or the literal 3s). It lets you write clean, intuitive code like:
co_await 250ms; // Suspend the coroutine for 250 milliseconds
...instead of manually managing timers and coroutine resumption logic.
Breakdown of the awaiter Struct
The core of any co_await operation is the awaiter—a type that defines three mandatory methods to control how the coroutine behaves when suspended and resumed.
1. Member Variable & Constructor
std::chrono::system_clock::duration duration;: Stores the amount of time we need to wait. We convert the inputdurationtosystem_clock's duration to ensure consistency across different clock types (e.g.,steady_clockvssystem_clock).- The constructor simply initializes this stored duration with the input value.
2. await_ready()
bool await_ready() const { return duration.count() <= 0; }
This is the first method the coroutine runtime calls.
- If it returns
true: The coroutine does NOT suspend—it continues executing immediately. This is an optimization for cases where waiting is unnecessary (e.g.,co_await 0s). - If it returns
false: The coroutine will suspend, and the runtime will move on toawait_suspend.
3. await_resume()
void await_resume() {}
This method runs right before the coroutine resumes execution.
- In this case, it's empty because we don't need to return any value to the coroutine after waiting. But you could easily modify it to return useful data—like the exact time the coroutine woke up:
Then you could use it like:std::chrono::system_clock::time_point await_resume() { return std::chrono::system_clock::now(); }auto wake_time = co_await 3s;
4. await_suspend(std::experimental::coroutine_handle<> h)
This is where the asynchronous waiting logic lives.
- The parameter
his a handle to the currently running coroutine. We need to hold onto this handle so we can resume the coroutine once the wait is done. - The example implementation uses a
std::jthreadto sleep for the specified duration, then callsh.resume()to wake the coroutine up.- Note: Using a thread per wait is not efficient for production code—real-world implementations use asynchronous timers (like Linux's
timerfd, Windows' timer queues, or a coroutine scheduler's built-in timer system) to avoid unnecessary thread overhead.
- Note: Using a thread per wait is not efficient for production code—real-world implementations use asynchronous timers (like Linux's
- If
await_suspendreturnsvoid(like here), the coroutine stays suspended untilh.resume()is called. You can also return abool(true= suspend,false= don't suspend) or anothercoroutine_handleto switch directly to another coroutine.
Common Questions & Answers
Q: Why convert to std::chrono::system_clock::duration?
A: Different clock types (e.g., steady_clock for monotonic time, system_clock for wall-clock time) have different duration definitions. Converting to system_clock's duration ensures we're using a consistent time base for waiting, and it aligns with most system timer APIs.
Q: Is this code compatible with C++20?
A: Almost. C++20 standardized coroutines, so you'll need to replace std::experimental::coroutine_handle with std::coroutine_handle (remove the experimental namespace). The rest of the logic works exactly the same.
Q: What if I want to cancel the wait?
A: To add cancellation support, you'd modify the awaiter to track a cancellation flag (like a std::atomic<bool>). In await_suspend, you'd check this flag before calling h.resume(), and provide a way to trigger cancellation from outside the coroutine.
Q: Can I use this with a custom coroutine scheduler?
A: Yes! Instead of spawning a thread, you'd pass the coroutine handle to your scheduler's timer system. For example, if your scheduler uses an event loop, you'd add a timer event that calls h.resume() when the duration elapses.
Full Working Example
Here's a complete, runnable example using the code above:
#include <iostream> #include <chrono> #include <experimental/coroutine> #include <thread> using namespace std::chrono_literals; template <class Rep, class Period> auto operator co_await(std::chrono::duration<Rep, Period> d) { struct awaiter { std::chrono::system_clock::duration duration; awaiter(std::chrono::system_clock::duration d) : duration(d) {} bool await_ready() const { return duration.count() <= 0; } std::chrono::system_clock::time_point await_resume() { return std::chrono::system_clock::now(); } void await_suspend(std::experimental::coroutine_handle<> h) { std::jthread([h, this]() { std::this_thread::sleep_for(duration); h.resume(); }).detach(); } }; return awaiter{std::chrono::system_clock::duration(d)}; } // Simple coroutine task type struct task { struct promise_type { task get_return_object() { return {}; } std::experimental::suspend_never initial_suspend() { return {}; } std::experimental::suspend_never final_suspend() noexcept { return {}; } void return_void() {} void unhandled_exception() { std::terminate(); } }; }; task delayed_greeting() { std::cout << "Hello from coroutine! Waiting 3 seconds...\n"; auto wake_time = co_await 3s; std::cout << "Woke up at: " << std::chrono::system_clock::to_time_t(wake_time) << "\n"; std::cout << "Delayed greeting: Hi there!\n"; } int main() { delayed_greeting(); // Keep main thread alive long enough for the coroutine to finish std::this_thread::sleep_for(4s); return 0; }
Run this, and you'll see the coroutine start, suspend for 3 seconds, then resume and print the final message.
内容的提问来源于stack exchange,提问作者Jive Dadson

