在使用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;
}
}