RTThread學習筆記——線程間通信學習


由通信提出的問題

  在裸機編程的過程中,我們經常會遇到函數需要另一些函數的數據信息,也就是通信,這時我們會怎么做呢?進行裸機開發的同學肯定都會說:使用全局變量,通過指針實現之類。使用全局變量快捷且高效。  

  但是在RTOS系統中,這會遇到一些問題:怎樣防止許多線程同時進行對這個變量的訪問?怎樣觀測通信是否已經發生,從而進行通信之后的工作?(例如:進行優先級轉換,或者進行數據的處理)如果有個線程比較磨蹭,通信遲遲不發生,CPU豈不是要尷尬地等待好久。。

  對於這些問題,操作系統給出了自己的通信方法與函數供使用者調用,當然,也最好使用操作系統的通信機制。

  

  我們打開RTT 的IDE,然后打開內核的Setting,會看到RTT的一些設定:

  

  可以看到,線程間通信一欄,這就是RTT給開發者提供的通信機制,在實際開發中,這些機制可以滿足絕大部分需求。RTT系統默認打開了信號量,互斥量,事件集,郵箱,消息隊列五種機制。


信號量:

二值信號量:

  在我們的裸機開發過程中,經常會使用一個變量:Flag,大多數時候,這個叫做Flag的變量會在0和1之間反復橫跳,用於標記某一件事情是否發生,並經常被用於中斷。這個Flag就是一個信號量,實際上,它是我們最常使用的通信方式之一。

  在RTT系統中,信號量會被獲取或者釋放(由獲取/釋放函數實現),當它被獲取時,就為0,當它被釋放,就為1。這樣的信號量被稱為二值信號量。

  舉一個例子(偽代碼):

static void xxx1()
{
    while(1)
    {
         /*一個傳感器采集一個特別復雜的數據,需要3S,不需要CPU參與*/
         xxx
    }
}

static void xxx2()
{
    while(1)
    {
         /*將這個數據交給CPU進行分析,數據無效就舍棄*/   
         xxx
      rt_thread_mdelay(xx)  } }

  在這個例子中,有兩個線程,線程2中,CPU一直在等待數據,但數據卻總是無效的(因為傳感器還沒采集到數據),這讓CPU一直在進行數據驗證,雖然CPU中間進行了一些延時,但仍然在無效數據上浪費了時間,這時,就需要信號量出馬了

 

  改良后數據如下(偽代碼):  

rt_sem_create(xxx);    //信號量創建函數,參數中默認為1,表示是釋放的,具體參數不贅述

static void xxx1()
{
    while(1)
    {
         rt_sem_take(xxx)    //獲取信號量,這時信號量被置0

         /*一個傳感器采集一個特別復雜的數據,需要3S,不需要CPU參與*/
         xxx

         rt_sem_release(xxx)  //釋放信號量,這時信號量被置1
    }
}

static void xxx2()
{
    while(1)
    {
         rt_sem_take(xxx)    //獲取信號量,當信號量已經被其他線程獲取,就進入阻塞態等待,等待時間自定,這里不贅述

         /*將這個數據交給CPU進行分析,數據無效就舍棄*/   
         xxx

         rt_sem_release(xxx)  //釋放信號量
   rt_thread_mdelay(xx) 
    }
}        

  這里我們看到,當傳感器開始工作時,信號量已經被獲取了,這時進入線程二想要處理數據,就會進入阻塞態,讓出CPU去做其他的事情,當傳感器獲得數據后,就會釋放信號量,這時,線程二就從阻塞態中退出,從而去處理有效的數據。(注意:獲取信號量進行相關操作后應立刻釋放信號量,如果不進行釋放,可能會導致其他線程無法運行)

  經過這一番操作,無效數據不會被一次又一次的處理了,而是在傳感器獲得數據后,CPU直接去處理有效的數據,節省了許多無效操作。

 

計數型信號量:

  計數型信號量與二值信號量類似,只不過范圍從二值的[0,1],變為了[0,65535],這樣它就可以被多個線程獲取,獲取的最大數目由使用者自定,使用的系統函數與二值信號量相同。

  計數信號量經常被用於控制一些公共資源的訪問數量,例如一個資源最多可以被x個線程同時訪問,多於x時效率大大下降或者會出現錯誤(有點像網站服務器限制訪客數量)。

 


 

 

互斥量:

   互斥量是一種特殊的二值信號量,其與二值信號量的最大區別為引入了優先級繼承機制。

  優先級繼承機制:如果一個高優先級的線程去申請一個已經被低優先級獲得的互斥量,高優先級會進入到阻塞態,而持有這個互斥量的線程地位會瞬間與高優先級一樣,取得高優先級相同的優先等級,這就是“優先級繼承”

  這樣有什么好處呢?這里依然是一個栗子。

static void HIGH()
{
    xxx
}

static void MIDDLE()
{
    xxx
}

static void LOW()
{
    xxx
}

  假設有三個線程,優先度分別為高,中,低。理所當然,開發者希望高優先級的越快處理越好,低優先級的則不是很緊要。

  這時,如果有個二值信號量,被低優先級(LOW)獲取。然后高優先級(HIGH)的也想要獲取這個信號量,於是進入了阻塞等待。

  但是當LOW准備用CPU運行時,MIDDLE卻發話了:我有更緊要的任務,你讓開。LOW小弟就被擠到了一邊,畢竟它是優先級最低的。

  這就產生了一個問題,開發者最希望運行的HIGH在等待信號量,而MIDDLE卻在運行(假設MIDDLE和HIGH無關聯,此時HIGH就要等待MIDDLE運行+LOW運行,而不是只等待LOW運行),LOW在等待CPU讓出。這顯然違背了開發者“高優先級越快越好”的想法。

  

  怎樣解決這個問題,這時就引入互斥量來解決這個問題,當HIGH進入阻塞態之后,LOW瞬間“繼承”了HIGH的優先級,LOW小弟也暫時體驗了當大哥的感覺,並把MIDDLE擠到一邊,快速的處理起了數據,並在處理完后變回原來的優先級,將互斥量釋放,HIGH得以繼續運行。這樣,通過暫時提高LOW的優先級,開發者所希望的HIGH處理越快越好的想法得以實現。

 

(暫時記錄信號量和互斥量的學習筆記)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM