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