930 字
5 分钟
协程Async_Server_Coroutine
协程
-
进程:单个程序表示为一个进程,是计算机资源分配和调度的最小单位。
进程内部拥有独立的内存空间、文件描述符和系统资源。
-
线程:进程内部划分的一个执行单元。
线程在会共享进程的内存空间,拥有独立的栈空间和寄存器状态。
-
协程:是一种用户态的轻量级线程,由程序自行地调度,无需操作系统介入。
协程内部通过主动地让出执行权,进行协程之间地切换,因此无需线程类似地栈空间和寄存器地切换开销。
1. 协程与线程的关系
- 协程(
通过 boost::asio::spawn
或yield_context
实现)是轻量级的异步任务,运行在io_context
的事件循环中,由io_context
绑定的线程调度。 - 线程数量由
AsioIOServicePool
的配置决定(即运行io_context::run()
的线程数) - 协程数量可以远超线程数量,因为协程是协作式调度的,类似用户态线程,不需要独占系统线程。
2. 协程数量大于线程数量时的处理
当协程数量(如多个 CSession 的协程)超过线程数量时:
调度机制:
io_context
维护一个事件队列,管理所有挂起的异步操作(包括协程的async_read
、async_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/