优秀的编程知识分享平台

网站首页 > 技术文章 正文

C++ 中coroutines的功能和实现(c++,cout)

nanyue 2024-09-02 19:03:14 技术文章 9 ℃

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 的协程为异步编程提供了强大的工具,但也需要开发者深入理解其机制,才能充分发挥其优势。

Tags:

最近发表
标签列表