930 字
5 分钟
协程Async_Server_Coroutine

协程#

  • 进程:单个程序表示为一个进程,是计算机资源分配和调度的最小单位。

    进程内部拥有独立的内存空间、文件描述符和系统资源。

  • 线程:进程内部划分的一个执行单元。

    线程在会共享进程的内存空间,拥有独立的栈空间和寄存器状态。

  • 协程:是一种用户态的轻量级线程,由程序自行地调度,无需操作系统介入

    协程内部通过主动地让出执行权,进行协程之间地切换,因此无需线程类似地栈空间和寄存器地切换开销。

1. 协程与线程的关系#

  • 协程(通过 boost::asio::spawnyield_context 实现)是轻量级的异步任务,运行在 io_context 的事件循环中,由 io_context 绑定的线程调度。
  • 线程数量由 AsioIOServicePool 的配置决定(即运行 io_context::run() 的线程数)
  • 协程数量可以远超线程数量,因为协程是协作式调度的,类似用户态线程,不需要独占系统线程。

2. 协程数量大于线程数量时的处理#

当协程数量(如多个 CSession 的协程)超过线程数量时:

调度机制:#

  • io_context 维护一个事件队列,管理所有挂起的异步操作(包括协程的 async_readasync_write 等)。
  • 每个线程调用 io_context::run(),从队列中取出就绪的事件(或协程的延续点)并执行。 协程在 yield(挂起)时暂停,等待 IO 事件完成;事件就绪后,协程恢复并继续执行。 协作式执行:
  • 同一线程内的多个协程通过 yield_context 协作切换。一个协程挂起(等待 IO)后,线程会切换到其他就绪的协程或事件。
  • 这确保即使协程数量远大于线程数量,系统也能高效处理,因为协程只在需要时占用线程。

并行性:#

  • 如果有多个 io_context(由 AsioIOServicePool 分配),每个 io_con`text 的线程独立运行,协程分布在不同线程上并行执行。
  • 如果只有一个 io_context 且单线程,多个协程在该线程内协作执行,表现为顺序调度。

3. 协程是否在每次异步 IO 时主动挂起?#

  • 每次执行异步 IO 操作(如 co_await boost::asio::async_read)时,协程会主动挂起。

  • co_await boost::asio::async_read(...) 使用 boost::asio::use_awaitable 作为完成令牌(completion token),这使得异步操作以协程的方式执行。

  • 当调用 co_await async_read 时,协程会将控制权交还给 io_context 的事件循环,挂起自身,等待 IO 操作完成(如数据到达或错误发生)。

  • 挂起是协程的主动行为,由 co_await 触发,允许其他协程或事件在同一线程上继续执行。

4. 如果不主动挂起,协程会被计算机主动挂起吗?#

答案:协程不会被计算机(或操作系统)主动挂起。

原因:

  • boost::asio 的协程基于 C++20 协程机制,属于 用户态协作式调度,不依赖操作系统线程的抢占式调度。
  • 协程的挂起和恢复完全由程序员通过 co_await 显式控制,或者由 boost::asio 的异步操作(如 async_read)在内部触发。
  • 如果代码中没有 co_await(即没有异步操作或显式挂起点),协程会像普通函数一样顺序执行,直到完成或遇到异常,不会自动挂起。
  • 计算机(操作系统)只负责调度线程,协程的挂起/恢复由 io_context 的事件循环在用户态管

程序员应当小心编写代码!如果没有co_await,协程默认就是顺序执行地。如果内部存在死循环,就可能导致阻塞#

协程Async_Server_Coroutine
https://chrisnake11.github.io/blog/posts/coding/network-programming/协程async_server_coroutine/
作者
Zheyv
发布于
2025-05-18
许可协议
CC BY-NC-SA 4.0