跳转至

Coroutines(协程)

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

1 什么是协程?

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

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

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

3 协程的使用场景

协程**不适合计算密集型**的场景,因为*单个线程内的协程是串行的,并不能利用多核能力*。
  1. I/O 阻塞型场景(包括网络传输、文件读写),当 I/O 阻塞时切换到另一个协程上执行。I/O 密集型需要多线程+协程模式。

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 个关键字

如果函数的定义进行了下列操作之一,那么它是协程:

  • 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;
}