C++20 中的协程:实现异步编程的全新方式

C++20 标准正式引入协程(coroutine)概念,为编写异步、非阻塞代码提供了更简洁、高效的语法与机制。与传统的线程或基于回调的异步模型相比,协程能够让代码保持同步式的书写风格,却在底层实现上通过轻量级的状态机实现挂起与恢复,从而显著降低上下文切换成本,提升系统吞吐量。下面,我们将从协程的基本语法、实现原理以及实际应用场景展开讨论,并给出完整示例。

1. 协程基本概念

协程是一种比线程更轻量的执行单元,它能够在任意位置挂起(co_awaitco_yieldco_return)并在需要时恢复执行。协程本身不需要像线程那样拥有完整的栈,只有在挂起点附近维护一个“状态机”状态,真正需要保留的局部变量会被“升到堆上”或保存在协程框架管理的缓存中。

C++20 对协程的支持主要体现在以下几个关键词上:

  • co_await:挂起当前协程,等待一个 Awaitable 对象完成后继续执行。
  • co_yield:将一个值返回给调用方,暂停协程。
  • co_return:结束协程并返回最终结果。
  • std::suspend_always / std::suspend_never:控制协程的挂起策略。

2. Awaitable 与协程句柄

一个可 await 的对象必须满足 Awaitable 概念,最重要的成员函数是:

bool await_ready();      // 是否立即完成
void await_suspend(std::coroutine_handle<> h); // 挂起时的动作
auto await_resume();     // 完成后返回的结果

当调用 co_await obj; 时,编译器会把 obj 的三函数分别调用,从而决定协程是否挂起。协程句柄 `std::coroutine_handle

` 可以用来手动恢复协程、查询状态或获取返回值。 ### 3. 一个简单的协程例子 下面演示一个 `async_sum` 函数,它接受两个整数,模拟异步延迟后返回它们之和。协程内部使用 `std::suspend_always` 进行挂起,然后在外部使用 `std::this_thread::sleep_for` 模拟耗时操作,最后通过 `co_return` 返回结果。 “`cpp #include #include #include #include struct AsyncSum { struct promise_type { int result_; std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } AsyncSum get_return_object() { return AsyncSum{ std::coroutine_handle ::from_promise(*this) }; } void return_value(int v) { result_ = v; } void unhandled_exception() { std::terminate(); } }; std::coroutine_handle coro_; explicit AsyncSum(std::coroutine_handle h) : coro_(h) {} ~AsyncSum() { if (coro_) coro_.destroy(); } int get() { if (!coro_.done()) coro_.resume(); return coro_.promise().result_; } }; AsyncSum async_sum(int a, int b) { // 模拟耗时操作 std::this_thread::sleep_for(std::chrono::milliseconds(100)); co_return a + b; } int main() { auto task = async_sum(3, 4); std::cout #include #include struct AsyncLineReader { struct promise_type { std::string line_; std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } AsyncLineReader get_return_object() { return AsyncLineReader{ std::coroutine_handle ::from_promise(*this) }; } void yield_value(std::string&& v) { line_ = std::move(v); } void return_void() {} void unhandled_exception() { std::terminate(); } }; std::coroutine_handle coro_; explicit AsyncLineReader(std::coroutine_handle h) : coro_(h) {} ~AsyncLineReader() { if (coro_) coro_.destroy(); } bool next() { if (coro_.done()) return false; coro_.resume(); return !coro_.done(); } std::string current() const { return coro_.promise().line_; } }; AsyncLineReader read_file(const std::string& path) { std::ifstream fin(path); std::string line; while (std::getline(fin, line)) { co_yield std::move(line); } } “` 使用示例: “`cpp auto reader = read_file(“data.txt”); while (reader.next()) { std::cout #include #include #include #include using namespace boost::asio; using awaitable_void = awaitable; awaitable_void read_line(tcp::socket& sock) { streambuf buf; std::size_t n = co_await async_read_until(sock, buf, ‘\n’); std::istream is(&buf); std::string line; std::getline(is, line); std::cout

发表评论