让线程既阻塞等待信号量又能执行周期任务的方法
程序员文章站
2024-02-22 18:23:04
...
在使用RTOS时,我们可能会遇到这样一种场景:一个线程既要阻塞等待信号量或邮箱,又要执行周期性任务。本文介绍一种简单的方法来实现该需求。
以信号量为例,一般的RTOS提供的信号量请求具有以下3种请求方法:
①. 无限等待,即一直阻塞等待信号量。
②. 超时等待,即设定一个超时时间,如果超时时间内还没有获取到信号量,则不再继续等待,线程继续执行。
③. 尝试等待,即尝试获取信号量,如果获取不到则线程立即继续执行。
我们可以使用超时等待的方法来实现线程既阻塞等待信号量,有能执行周期性任务,体的思路如下:将周期性任务的剩余超时时间作为要等待的信号量的超时时间,如果在超时时间内没有获取到信号量,就会退出信号量的等待,此时周期性任务的超时时间也刚好到了,可以执行一次周期性任务;如果在超时时间内获取到了信号量,则先处理信号量然后重新计算周期性任务的剩余超时时间,然后以该时间为信号量的超时时间重新等待信号量。流程图如下。
使用这种方法,既不会错过信号量消息,又可以定时执行周期任务,一举两得。事实上,LWIP中就使用了这种方法,以下代码简化了源码,代码注释中提到的超时事件就是上文指的周期任务。
static void tcpip_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
{
u32_t sleeptime, res;
again:
LWIP_ASSERT_CORE_LOCKED();
/* 获取超时事件的剩余超时时间 */
sleeptime = sys_timeouts_sleeptime();
/* 如果超时事件的超时时间是永久,那么永久等待信号量 */
if (sleeptime == SYS_TIMEOUTS_SLEEPTIME_INFINITE)
{
UNLOCK_TCPIP_CORE();
sys_arch_mbox_fetch(mbox, msg, 0);
LOCK_TCPIP_CORE();
return;
}
/* 如果超时事件的超时时间已到,执行一次超时处理 */
else if (sleeptime == 0)
{
sys_check_timeouts();
goto again;
}
UNLOCK_TCPIP_CORE();
/* 以sleeptime作为信号量的请求超时,请求信号量 */
res = sys_arch_mbox_fetch(mbox, msg, sleeptime);
LOCK_TCPIP_CORE();
/* 信号量等待超时,说明超时事件刚好超时,执行一次超时处理 */
if (res == SYS_ARCH_TIMEOUT)
{
sys_check_timeouts();
goto again;
}
}