C/C++协程学习笔记丨C/C++实现协程及原理分析视频( 五 )
如果本次操作提供了 Timeout 参数 , co_poll 还会将协程 CC 本次操作对应的 stPoll_t 加入到定时器队列中 。 这表明在 Timeout 定时触发之后 , 也会唤醒协程 CC 的执行 。 当整个上半段都完成后 , co_poll 立即调用 co_yield_env 让出 CPU , 执行流程跳转回到 main 协程中 。
从上面的流程图中也可以看出 , 当执行流程再次跳回时 , 表明协程 CC 添加的读写等监听事件已经触发 , 即可以执行相应的读写操作了 。 此时 CC 首先将其在上半段中添加的监听事件从 Epoll 中删除 , 清理残留的数据结构 , 然后调用读写逻辑 。
定时器实现
协程 CC 在将一组 fds 加入 Epoll 的同时 , 还能为其设置一个超时时间 。 在超时时间到期时 , 也会再次唤醒 CC 来执行 。 libco 使用 Timing-Wheel 来实现定时器 。 关于 Timing-Wheel 算法 , 可以参考 , 其优势是 O(1) 的插入和删除复杂度 , 缺点是只有有限的长度 , 在某些场合下不能满足需求 。
文章插图
回过去看 stCoEpoll_t 结构 , 其中 *pTimeout 代表时间轮 , 通过函数 AllocateTimeout 初始化为一个固定大小(60 * 1000)的数组 。 根据 Timing-Wheel 的特性可知 , libco 只支持最大 60s 的定时事件 。 而实际上 , 在添加定时器时 , libco 要求定时时间不超过 40s 。 成员 pstTimeoutList 记录在 co_eventloop 中发生超时的事件 , 而 pstActiveList 记录当前活跃的事件 , 包括超时事件 。 这两个结构都将在 co_eventloop 中进行处理 。
下面我们简要分析一下加入定时器的实现:
int AddTimeout( stTimeout_t *apTimeout, stTimeoutItem_t *apItem,
unsigned long long allNow )
{
if( apTimeout->ullStart == 0 ) // 初始化时间轮的基准时间
{
apTimeout->ullStart = allNow;
apTimeout->llStartIdx = 0; // 当前时间轮指针指向数组0
}
// 1. 当前时间不可能小于时间轮的基准时间
// 2. 加入的定时器的超时时间不能小于当前时间
if( allNow < apTimeout->ullStart || apItem->ullExpireTime < allNow )
{
return __LINE__;
}
int diff = apItem->ullExpireTime - apTimeout->ullStart;
if( diff >= apTimeout->iItemSize ) // 添加的事件不能超过时间轮的大小
{
return __LINE__;
}
// 插入到时间轮盘的指定位置
AddTail( apTimeout->pItems +
(apTimeout->llStartIdx + diff ) % apTimeout->iItemSize, apItem );
return 0;
}
定时器的超时检查在函数 co_eventloop 中执行 。
EPOLL 事件循环main 协程通过调用函数 co_eventloop 来监听 Epoll 事件 , 并在相应的事件触发时切换到指定的协程执行 。 有关 co_eventloop 与 应用协程的交互过程在上一节的流程图中已经比较清楚了 , 下面我们主要介绍一下 co_eventloop 函数的实现:
上文中也提到 , 通过 epoll_wait 返回的事件都保存在 stCoEpoll_t 结构的 co_epoll_res 中 。 因此 co_eventloop 首先为 co_epoll_res 申请空间 , 之后通过一个无限循环来监听所有 coroutine 添加的所有事件:
for(;;)
{
int ret = co_epoll_wait( ctx->iEpollFd,result,stCoEpoll_t::_EPOLL_SIZE, 1 );
...
}
对于每一个触发的事件 , co_eventloop 首先通过指针域 data.ptr 取出保存的 stPollItem_t 结构 , 并将其添加到 pstActiveList 列表中;之后从定时器轮盘中取出所有已经超时的事件 , 也将其全部添加到 pstActiveList 中 , pstActiveList 中的所有事件都作为活跃事件处理 。
对于每一个活跃事件 , co_eventloop 将通过调用对应的 pfnProcess 也就是上图中的OnPollProcessEvent 函数来切换到该事件对应的 coroutine , 将流程跳转到该 coroutine 处执行 。
最后 co_eventloop 在调用时也提供一个额外的参数来供调用者传入一个函数指针 pfn 。 该函数将会在每次循环完成之后执行;当该函数返回 -1 时 , 将会终止整个事件循环 。 用户可以利用该函数来控制 main 协程的终止或者完成一些统计需求 。
- 用于|用于半监督学习的图随机神经网络
- 今日|“舜网”学习强国号今日上线 济南报业全媒体矩阵再添新成员
- SK|SK电讯推出自研AI芯片SAPEON X220 深度学习计算速度是常用GPU 1.5倍
- 效果|这个让你相见恨晚的技巧,能让PPT排版更加有设计感,推荐学习
- 菜鸟学编程,不懂C++ this指针?还不赶快来学一学
- 学习C语言的软件,就突然被我绿了?
- Linux 之父对 C++ 进行了炮轰,C++不值得推荐?
- 学习python第二弹
- 喵喵机错题打印机P1:随时打印,随时学习,快速整理错题
- 爱可可AI论文推介(10月17日)