跳转至

Coroutines(协程)

title: gcc支持情况?
gcc 10 支持协程,需要添加选项 -fcoroutines打开,默认不启用。
title: C++20 协程特点?
协程是**无栈的**,它们通过返回到调用方暂停执行,并且恢复执行所需的数据与栈分离存储。这样就可以编写异步执行的顺序代码(例如不使用显式的回调来处理非阻塞 I/O),还支持作用于惰性计算的无限序列上的算法及其他用途。

1 什么是协程?

协程是一个 函数,它是能 暂停(suspend)执行以在之后 恢复(resume)的函数。

2 协程与进程、线程的比较

  1. 协程既不是进程,也不是线程,协程仅仅是一个 特殊的函数,协程跟他们就不是一个维度。
  2. 一个进程可以包含多个线程,一个线程可以包含多个协程。
  3. 一个线程内的多个协程虽然可以切换,但是这 多个协程是串行执行 的,只能在这一个线程内运行,没法利用 CPU 多核能力。
  4. 协程与进程一样,它们的切换都存在 上下文切换 问题。(协程的切换是在用户态的,不会陷入内核)

3 协程的使用场景

1. 协程**不适合计算密集型**的场景,因为*单个线程内的协程是串行的,并不能利用多核能力*。
2. 协程适合**I/O 阻塞型场景**(包括网络传输、文件读写),当 I/O 阻塞时切换到另一个协程上执行。I/O 密集型需要多线程 + 协程模式。
  1. 异步编程:协程最常见的用途之一是异步编程。通过将异步任务包装在协程中,您可以编写看起来像同步代码的程序,而不必手动管理回调函数或线程。
  2. 网络编程:协程在处理网络请求和响应时非常有用。您可以使用协程来编写服务器和客户端代码,使其在等待输入或输出时能够非阻塞地执行其他任务。
  3. 文件 IO:协程可以用于异步文件读写操作。这对于需要处理大型文件或需要同时进行多个文件操作的应用程序非常有用。
  4. 事件循环:事件循环是一种处理异步事件的常见模式。协程可以与事件循环结合使用,以响应和处理各种事件,如 GUI 事件、网络套接字事件、定时器事件等。
  5. 并发控制:协程可以用于简化并发控制和任务调度。通过将并发任务封装在协程中,可以更容易地实现复杂的并发模型。
  6. 生成器:协程可以用于创建生成器函数,使其能够在需要时生成值。这对于遍历大型数据集或生成无限序列非常有用。
  7. 协同任务:协程还可以用于实现协同任务,其中多个协程之间可以协同工作,共享状态或通信以完成任务。
  8. 状态机:协程可以用于实现状态机,其中每个状态都表示为一个协程。这可以使状态迁移和事件处理变得更加清晰和模块化。

4 libco IO 工作流程

  • libco 工作流程
  • 将阻塞操作改变为 非阻塞,sql 命令往数据库发送,其实就是往一个 fd 上写数据。
  • 数据发出去后非阻塞操作马上返回。然后将这个 fd 挂在 epoll 上(当发出去的命令返回或者超时,epoll_wait 会返回通知的)。
  • 保存当前(协程)程序运行时的上下文,这样就相当于 yield 切走了当前(协程)程序 A。
  • 然后加载其它等待执行的(协程)程序 B 的上下文,返回上一次 B 执行源码的下一条源码地址去运行,这样 A 在等待 sql 结果返回的过程中,被唤醒的 B 仍然能继续工作,B 如果遇到阻塞 IO 也同样执行上述步骤。
  • sql 命令返回结果,epoll_wait 收到通知返回,resume 唤醒 fd 对应的 A(协程)程序。

5 C++20 协程编写

5.1 3 个关键字 co_await、 co_return 和 co_yield

C++20 标准规定,具有 co_await、 co_return 和 co_yield 关键字中任意一个的函数就是协程。请注意,因为 main 函数不能为协程,所以函数体中不能出现这 3 个关键字。通常情况下,建议将协程和标准库中的 future、generator 一起使用,因为协程辅助代码较为复杂,所以应该尽量避免自定义它们。 如果函数的定义进行了下列操作之一,那么它是协程: - 用 co_await 运算符暂停执行,直到恢复:

task<> tcp_echo_server() {
  char data[1024];
  while (true) {
    std::size_t n = co_await socket.async_read_some(buffer(data));
    co_await async_write(socket, buffer(data, n));
  }
}
  • 用关键词 co_yield 暂停执行并返回一个值:
generator<int> iota(int n = 0) {
  while(true)
    co_yield n++;
}
  • 用关键词 co_return 完成执行并返回一个值:
lazy<int> f() {
  co_return 7;
}