C++20,boost协程

协程(Coroutines)详解

1. 协程的基本概念

  • 暂停和恢复: 协程可以在某个点暂停执行,并在稍后从暂停的地方继续执行。这与传统的函数调用不同,传统函数一旦开始执行就会一直运行到结束。
  • 异步编程: 协程非常适合处理异步操作,比如网络请求、文件I/O等。它们可以在等待I/O操作完成时暂停,从而避免阻塞线程。
  • 轻量级: 协程比线程更轻量级,因为它们不需要操作系统的线程管理,切换上下文的开销也更小。

2. 协程的执行流程

  • 启动协程 : 使用 co_spawn 启动协程,并将其提交给执行器。
  • 遇到 co_await : 协程在 co_await 处暂停执行,将控制权交还给执行器。
  • 异步操作继续进行: 异步操作在后台继续进行,不会阻塞线程。执行器可以调度其他任务或协程。
  • 恢复执行: 当异步操作完成时,执行器将控制权交还给协程,协程从暂停的地方继续执行。

3. 关键概念和术语

co_await
  • 作用: 用于在协程中等待一个异步操作完成。它会暂停协程的执行,直到被等待的操作完成,然后恢复执行。
awaitable
  • 定义 : 表示可以被 co_await 等待的对象,封装了异步操作的状态和结果。
  • 作用 : 与 co_await 一起使用,协程会在 co_await 处暂停,直到 awaitable 对象表示的异步操作完成。
co_spawn
  • 作用: 启动一个新的协程。接受一个执行器(executor)、一个协程函数和一个分离策略(detached),并启动协程的执行。
执行器(executor)
  • 定义 : 用于管理协程执行的对象。它负责调度协程的执行。在 Boost.Asio 中,io_context 就是一个执行器。
use_awaitable
  • 作用 : 一个标记,用于指示异步操作应该返回一个 awaitable 对象,从而可以在协程中使用 co_await 来等待这些操作的完成。

4. 示例代码解析

echo 协程
cpp 复制代码
awaitable<void> echo(ip::tcp::socket socket)
{
    try
    {
        char data[1024];
        for (;;)
        {
            std::size_t n = co_await socket.async_read_some(buffer(data), use_awaitable);
            co_await async_write(socket, buffer(data, n), use_awaitable);
        }
    }
    catch (const std::exception& e)
    {
        std::cout << "Exception is " << e.what() << std::endl;
    }
}
  • 功能: 实现一个简单的回显服务器,接收客户端发送的数据并将其原样返回。
  • 流程 : 使用 co_await 等待异步读取和写入操作,通过无限循环持续处理客户端的数据,捕获并打印任何异常。
listener 协程
cpp 复制代码
awaitable<void> listener()
{
    auto executor = co_await this_coro::executor;
    ip::tcp::acceptor acceptor(executor, { ip::tcp::v4(), 10086 });
    for (;;)
    {
        ip::tcp::socket socket = co_await acceptor.async_accept(use_awaitable);
        co_spawn(executor, echo(std::move(socket)), detached);
    }
}
  • 功能: 负责监听端口并接受新的TCP连接。
  • 流程 : 获取当前协程的执行器,创建TCP接受器,使用 co_await 等待异步接受连接,每当接受到一个新的连接时,使用 co_spawn 启动一个新的 echo 协程来处理该连接。
main 函数
cpp 复制代码
int main()
{
    try
    {
        io_context io_context(1);
        signal_set signals(io_context, SIGINT, SIGTERM);
        signals.async_wait(& 
            {
                io_context.stop();
            }
        );
        co_spawn(io_context, listener(), detached);
        io_context.run();
    }
    catch (const std::exception& e)
    {
        std::cout << "Exception is " << e.what() << std::endl;
    }
}
  • 功能: 程序的入口点,负责初始化I/O上下文并启动监听协程。
  • 流程 : 创建 io_context 对象,设置信号处理器,捕捉 SIGINT 和 SIGTERM 信号,并在接收到信号时停止 io_context,使用 co_spawn 启动 listener 协程,调用 io_context.run() 开始事件循环,处理异步操作。

5. 协程与线程的对比

  • 协程: 轻量级、用户态、非抢占式,适用于I/O密集型任务。
  • 线程: 重量级、内核态、抢占式,适用于CPU密集型任务。

6. 常见问题解答

  • 为什么在 co_spawn 中传递 coroutine() 而不是 coroutine : 传递 coroutine() 是因为 co_spawn 需要一个 awaitable 对象,而不是一个可调用对象。coroutine() 调用协程函数并返回一个 awaitable 对象。
相关推荐
开开心心就好1 小时前
音频降噪剪辑工具,音量裁剪一键处理
运维·服务器·前端·spring·ocr·音视频·1024程序员节
一枕槐安4871 小时前
大模型限流、重试、超时处理:API 调用健壮性方案
linux·服务器·php·大模型api·指数退避·限流控制·超时预算
黑十1 小时前
PyTorch 报 “no CUDA GPUs are available“ 怎么办?3 步排查
服务器·网络·人工智能·pytorch·python
Ruiery3 小时前
Linux 6.6内核并发与同步(五):mutex 可睡眠锁
linux·运维·服务器
开开心心就好3 小时前
竖式除法出题工具,整除余数混合模式
运维·服务器·前端·javascript·ocr·perl·1024程序员节
updayday8544 小时前
客户附件不该发往旧邮箱,如何在发送前拦截 - Ping64 邮件安全管控
大数据·网络·数据库·安全·智能路由器
其实防守也摸鱼4 小时前
Chrome 扩展安全攻防实录:从攻击面到防御实践
开发语言·前端·网络·chrome·安全·web安全·php
小茬粥4 小时前
QEMU 宿主机网卡实例发现与采集脚本(标准 Linux / Proxmox VE 适配版)COLT_CMDB_QEMU_IFACE.sh
网络
见合八方4 小时前
借助半导体光放大器实现功率预算超31dB的112.5Gbit/s长距无源光网络
网络·5g·soa·光通信·激光雷达·半导体光放大器
程序猿老A5 小时前
轻量应用服务器总是CPU 100%怎么办?
服务器