ST源码分析-st_usleep - 弦外之音

/ 0评 / 0

由于 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 协程的运行。



下面就来分析一次 st_usleep() 的实现原理。

重点如下图:

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

从之前的文章中,我们知道 _ST_SWITCH_CONTEXT() 会不断地调 _st_vp_schedule() 来调度处理其他协程,处理完其他协程之后,就会进入 _st_idle_thread_start()

也就是说,b 协程跑完之后,就会进入 _st_idle_thread_start(),请看下图:

从上图可以看到,_st_vp_check_clock() 就是处理 进入 _ST_ST_SLEEPING 状态的协程。代码如下图:

_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毫秒 就会立即返回。

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 状态的协程就能继续跑。


不过上面的这种 sleep 实现,会有一个问题,因为是处理完其他协程才会进入 _st_idle_thread_start(),如果其他的协程是计算密集型的,消耗太多的时间,就没法及时唤醒 处于 sleep 中的协程。

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


由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1。QQ:2338195090。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注