打印
[其他ST产品]

ST源码分析-st_usleep

[复制链接]
714|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
由于 ST 库是单线程程序,如果使用了 unistd.h 里面的 sleep() 或者 usleep() 函数,就会阻塞所有协程函数的执行。但是有时候,某个协程确实要 sleep 休眠一些时间,那怎么办?

ST 库提供了 st_usleep() 函数,使用这个函数,就可以让某个协程休眠一段时间,但是又不会阻塞其他的协程函数的执行。下面就用一小段代码演示一下 st_usleep() 的用法,代码如下,为了简洁,省略了错误处理。

#include <stdio.h>
#include "st.h"
#include "../common.h"

void *print_a(void *arg) {
    st_usleep(2 * 1000000LL);
    st_utime_t time_now = st_utime();
    printf("I am a , %lld\r\n", time_now);
    return NULL;
}

void *print_b(void *arg) {
    st_utime_t time_now = st_utime();
    printf("I am b , %lld\r\n", time_now);
    return NULL;
}

int main(int argc, char *argv[]) {
    st_init();

    st_thread_create(print_a, NULL, 0, 0);
    st_thread_create(print_b, NULL, 0, 0);

    st_thread_exit(NULL);

    /* NOTREACHED */
    return 1;
}
执行之后的结果如下图:




从上图看到,b 协程 sleep 并不会阻塞 a 协程的运行。
————————————————
版权声明:本文为CSDN博主「Loken2020」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012117034/article/details/124665316

使用特权

评论回复
沙发
慢动作|  楼主 | 2023-5-25 15:56 | 只看该作者
下面就来分析一次 st_usleep() 的实现原理。

重点如下图:



上面的代码,把 a 协程状态改成 _ST_ST_SLEEPING ,然后把 a 协程加进去 SLEEPQ 队列,然后就 开始用 _ST_SWITCH_CONTEXT() 切换协程,此时 a 协程就会被挂起,因为 系统线程 已经切换到别的地方运行了, 所以 a 协程看起来是阻塞了。

使用特权

评论回复
板凳
慢动作|  楼主 | 2023-5-25 15:57 | 只看该作者
我们知道 _ST_SWITCH_CONTEXT() 会不断地调 _st_vp_schedule() 来调度处理其他协程,处理完其他协程之后,就会进入 _st_idle_thread_start()。

也就是说,b 协程跑完之后,就会进入 _st_idle_thread_start(),请看下图: 从上图可以看到,_st_vp_check_clock() 就是处理 进入 _ST_ST_SLEEPING 状态的协程。

使用特权

评论回复
地板
慢动作|  楼主 | 2023-5-25 15:57 | 只看该作者
代码如下图:

使用特权

评论回复
5
慢动作|  楼主 | 2023-5-25 15:58 | 只看该作者
_st_vp_check_clock() 函数有3个重点:

1,变量 elapsed 没有用到。

2,使用 if (thread->due > now) 来判断 时间是否到了,这里 thread->due 的实现非常有趣,due 是在 _st_add_sleep_q() 加入 SLEEPQ 队列的时候赋值的,如下: 下面就来看看 _ST_LAST_CLOCK 是什么时候赋值的,如下图: 从上图可以看到,_ST_LAST_CLOCK 是在上一次调度的时候赋值的。因此,如果 从上一次上下文切换到现在已经过去了8毫秒,而 st_usleep() 为10毫秒,那么 在 调用 st_usleep() 之后,可能 再过2毫秒 就会立即返回。

使用特权

评论回复
6
慢动作|  楼主 | 2023-5-25 15:58 | 只看该作者
3,_ST_DEL_SLEEPQ(thread); -> thread->state = _ST_ST_RUNNABLE -> _ST_ADD_RUNQ(thread)

主要就上面这几行代码,时间到了,就把 协程从 SLEEPQ 删除,把协程状态改成 _ST_ST_RUNNABLE,再加进去 RUNQ 队列, 然后下次 _st_vp_schedule() 调度, _ST_ST_RUNNABLE 状态的协程就能继续跑。

使用特权

评论回复
7
慢动作|  楼主 | 2023-5-25 16:05 | 只看该作者
不过上面的这种 sleep 实现,会有一个问题,因为是处理完其他协程才会进入 _st_idle_thread_start(),如果其他的协程是计算密集型的,消耗太多的时间,就没法及时唤醒 处于 sleep 中的协程。

操作系统 unistd.h 里面的 sleep() 或者 usleep() 函数 应该类似的问题,如果其他线程太耗时,CPU爆满,会没法及时切换来唤醒 sleep中的线程。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

63

主题

695

帖子

0

粉丝