C++20 引入了协程(coroutines),这是一种允许函数在执行过程中暂停和恢复的新特性。协程提供了一种编写异步代码的简洁方法,使得代码看起来像是同步执行的,但实际上是在异步执行。主要的协程关键字包括 `co_await`、`co_yield` 和 `co_return`。
01 核心组件
协程句柄(coroutine handle):表示协程本身,允许控制协程的执行。
承诺对象(promise object):管理协程的状态和结果。
等待者对象(awaiter object):处理协程的挂起和恢复逻辑。
02 应用举例
- 异步等待示例
下面的示例展示了如何使用 `co_await` 关键字等待异步任务完成。
#include <iostream>
#include <coroutine>
#include <chrono>
#include <thread>
#include <future>
// 自定义awaiter对象
struct Sleeper {
std::chrono::seconds duration;
bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle<> handle) const {
std::thread([handle, this] {
std::this_thread::sleep_for(duration);
handle.resume();
}).detach();
}
void await_resume() const noexcept {}
};
// 使用协程等待异步操作完成
std::future<void> performTask() {
std::cout << "Task started..." << std::endl;
co_await Sleeper{std::chrono::seconds(2)};
std::cout << "Task completed after 2 seconds!" << std::endl;
}
int main() {
auto future = performTask();
future.get(); // 等待协程完成
return 0;
}
在这个示例中,`Sleeper` 是一个自定义的 `awaiter` 对象,用于挂起协程2秒钟。`performTask` 使用 `co_await` 等待 `Sleeper` 完成,然后继续执行。
- 生成器
协程也可以用于实现生成器,允许在生成值时暂停和恢复执行。
#include <iostream>
#include <coroutine>
template<typename T>
struct generator {
struct promise_type {
T current_value;
std::suspend_always yield_value(T value) {
current_value = value;
return {};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
generator get_return_object() { return generator{std::coroutine_handle<promise_type>::from_promise(*this)}; }
void return_void() {}
void unhandled_exception() { std::terminate(); }
};
std::coroutine_handle<promise_type> handle;
generator(std::coroutine_handle<promise_type> h) : handle(h) {}
~generator() { if (handle) handle.destroy(); }
bool next() {
handle.resume();
return !handle.done();
}
T value() {
return handle.promise().current_value;
}
};
generator<int> count_to_ten() {
for (int i = 1; i <= 10; ++i) {
co_yield i;
}
}
int main() {
auto gen = count_to_ten();
while (gen.next()) {
std::cout << gen.value() << std::endl; // 输出1到10
}
return 0;
}
03 对比coroutines与其它语言类似功能的异同
- Python
Python 使用 `async` 和 `await` 关键字实现协程。与 C++ 相似,Python 的协程也是在同一个线程中执行,不会创建新的线程。
相似点:
- 都使用 `await` 关键字挂起协程。
- 都可以在不阻塞线程的情况下执行异步操作。
不同点:
- Python 协程集成更紧密,标准库提供了丰富的异步工具,如 `asyncio`。
- C++ 协程需要手动定义承诺对象和等待者对象,Python 则较为自动化。
Python 示例:
import asyncio
async def async_add(a, b):
return a + b
async def main():
result = await async_add(1, 2)
print(f"Result: {result}")
asyncio.run(main())
- Kotlin
Kotlin 使用 `suspend` 关键字定义挂起函数,并通过 `launch` 和 `async` 构建器启动协程。
相似点:
- 使用 `await` 等类似机制来挂起和恢复协程。
- 提供非阻塞的异步编程模型。
不同点:
- Kotlin 的协程更加集成和简洁,支持结构化并发,简化协程管理。
- C++ 的协程提供更低层次的控制和灵活性,但也增加了复杂性。
Kotlin 示例:
import kotlinx.coroutines.*
suspend fun asyncAdd(a: Int, b: Int): Int {
return a + b
}
fun main() = runBlocking {
val result = asyncAdd(1, 2)
println("Result: $result")
}
04 corountines 的应用场景和注意事项
- 应用场景
- 网络编程:通过协程实现高效的异步 I/O 操作,如处理大量并发连接的服务器。
- 图形界面:在 GUI 应用中使用协程处理异步事件,避免界面卡顿。
- 游戏开发:在游戏引擎中使用协程处理复杂的异步任务,如 AI 行为和物理计算。
- 注意事项
- 性能开销:虽然协程比线程更轻量,但仍有一定的开销,需要合理使用。
- 异常处理:确保在协程中正确处理异常,避免未捕获的异常导致程序崩溃。
- 资源管理:合理管理协程生命周期,避免资源泄漏和僵尸协程。
总之,C++20 的协程为异步编程提供了强大的工具,但也需要开发者深入理解其机制,才能充分发挥其优势。