RT-Thread學習筆記 --(6)RT-Thread線程間通訊學習過程總結

前兩篇文章總結了RT-Thread多線程以及多線程同步的學習過程,關於前兩篇學習總結,能夠查看以前的文章。html

本篇文章繼續總結關於RT-Thread多線程相關的最後一個重要知識點:線程間通訊。前面的文章屢次說起到,一個大的任務拆分爲多個小任務,這些小任務之間必然存在着各類各樣的關係,致使這些小任務的線程不能各自爲政,必需要考慮其餘任務線程的運行狀況。git

既然已經有了線程間同步,可讓多個線程之間進行相互溝通,那爲啥還須要線程間通訊呢?線程間通訊究竟是什麼東西,這種方式有什麼應用場景?github

關於多線程之間的通訊,RT-Thread官方提供了比較豐富的文檔做爲參考,具體能夠查看如下連接:https://www.rt-thread.org/doc...多線程

本文嘗試從如下幾個方面總結一下RT-Thread線程間通訊的學習過程
image
線程間通訊的相關概念異步

什麼是線程間通訊?通訊,顧名思義,就是雙方須要進行溝通與對話。通俗地歸納,就是A線程在工做運行期間,有某些數據或者信息,要告訴B線程,讓B線程接收到這些數據或信息後,可以繼續完成指定的任務和工做。函數

兩個線程之間爲何要進行通訊呢?仍是那句話,多個任務線程並非獨立的,它們在工做的時候是須要根據業務場景進行必定的溝通的,仍是以音樂播放器舉例,當歌詞讀取線程把歌詞從硬盤裏面讀出來了,要把這一串讀到的歌詞告訴給顯示線程,讓它把歌詞顯示出來。這個「告訴」的動做,就是經過線程間通訊來進行的。
2.png
既然都是爲了協調線程的工做狀態,線程間同步和線程間通訊這二者有什麼區別呢?區別就是線程間同步能作的事情太有限了,線程間同步只是告訴一下對方「別跑太快,等等我嘛~」,而線程間通訊,就是有一大堆的數據和信息要告知對方,萬一A線程有不少話要跟B線程說,線程同步這種方式就不能知足要求了,因此須要線程間通訊。
image
線程間通訊的方式學習

針對RT-Thread實時操做系統,線程間通訊主要有三種方式:郵箱,消息隊列,信號。這三種線程間通訊機制都有各自的特色,在實際開發工做裏面,須要根據不一樣的應用場景進行區分使用。url

郵箱是線程間通訊的其中一種方式,這個郵箱的概念,跟咱們生活中使用的郵箱概念,實際上是大同小異的,在生活中,若是咱們有信件要寄,就把信件往郵筒一扔就能夠了,郵局會負責把信件送往目的地。操作系統

一樣的道理,當A線程有信件(即數據)要發送給B線程,只須要調用操做系統提供的郵箱相關接口函數,把數據發送出去,操做系統就會負責把數據轉發到目標線程,整個轉發過程是怎樣實現的,收和發的線程都不須要關心。.net

使用郵箱進行線程間通訊,特色是開銷低,效率高。這是由於,每一個郵件信息最多隻能是4個字節的內容,因此,這個郵件信息能夠是某個數據塊的指針,經過指針傳遞的方式,來傳輸更多的數據。
image

郵箱在使用過程當中,可能會存在郵箱空或郵箱滿的狀況,在郵箱空的狀況下,接收郵件的線程會選擇掛起等待,或者等超時時間到來。在郵箱滿的狀況下,發送郵件的線程會選擇掛起或直接返回一個郵箱滿的返回值。

系統內核提供如下郵箱相關的API函數接口,以下圖所示。
image
消息隊列是另一種比較經常使用的線程間通訊方式,至關於郵箱的擴展。跟郵箱不一樣的是,消息隊列是能夠接收不定長的數據的,而且把這個不定長的數據複製到自身線程的內存空間。

消息隊列其實就是一個數據存儲空間,這個存儲空間遵循先進先出的原則,也就是說,不論是什麼消息,等待消息的線程得到的是最早進入隊列的消息。

消息隊列控制塊裏面,其實有兩個鏈表,一個鏈表是用來掛接空的消息塊(也就是沒有內容的消息隊列),另外一個鏈表是用來掛接存有消息的消息塊,具體抽象以下圖所示。
image

當線程A要發送一個消息時,先從空閒消息塊鏈表取出一個塊空間,把消息裝進去後,把這個消息塊掛接到非空消息塊鏈表的隊尾。若是使用緊急方式發送消息,則把該消息塊掛接到非空消息鏈表的隊首。線程獲取消息的時候,老是會獲取鏈表頭的消息的。

系統內核提供如下消息隊列相關的API函數接口,以下圖所示。
image

信號,在軟件層次上其實至關於一種軟中斷的方式,這種中斷機制是操做系統模擬出來的,一個線程收到一個信號,跟硬件處理器收到一個硬件中斷請求,這個過程基本上是相似的。

當一個線程在正常運行期間,若是其餘線程有突發的事件或異常通知須要處理,就能夠經過信號的方式發送出去,線程在正常運行期間不須要等待信號的到來(由於不知道信號何時會到來)。

收到信號的線程,對各類信號的處理有如下三種方法:
一、相似中斷的處理程序,能夠針對須要處理的信號指定處理函數,由該函數來處理。
二、直接忽略某個信號,對該信號不作任何處理,就像未發生過同樣。
三、使用系統保留的默認值來處理該信號。
image

系統內核提供如下信號相關的API函數接口,以下圖所示。
image
多線程通訊的應用示例

多線程通訊的應用示例,主要是爲了驗證郵箱,消息隊列,信號的API接口函數,而且經過實驗現象觀察這三種線程通訊方式的運行狀況。

示例源碼下載連接:https://github.com/embediot/r...

郵箱示例主要是初始化了2個靜態線程,一個靜態的郵箱對象,線程 2 發送郵件,共發送 11 次,線程 1 接收郵件,共接收到 11 封郵件,將郵件內容打印出來,並判斷結束。

消息隊列示例主要初始化了2個靜態線程,線程 1 會從消息隊列中收取消息,線程 2 定時給消息隊列發送普通消息和緊急消息。因爲線程 2 發送消息 「I」 是緊急消息,會直接插入消息隊列的隊首,因此線程 1 在接收到消息 「B」 後,接收的是該緊急消息,以後才接收消息「C」。

信號示例主要是建立了 1 個線程,在安裝信號時,信號處理方式設爲自定義處理,定義的信號的處理函數爲 thread1_signal_handler(),待此線程運行起來安裝好信號以後,給此線程發送信號,此線程將接收到信號,並打印信息。

具體示例的實現能夠查看工程源碼,在thread_communication.h頭文件中,打開相應的宏定義開關,從新編譯工程並下載到開發板便可。
image
線程間通訊的注意事項

在進行多線程間通訊的時候,關於郵箱、消息隊列、信號這三種線程間通訊方式,有如下一些注意事項:

1.使用郵箱進行線程間通訊時,因爲一封郵件最多隻能是4個字節長度,所以若是要傳遞較多數據信息,可使用結構體進行信息封裝,經過指針方式進行傳遞。

2.郵件發送是非阻塞的,所以能夠應用於中斷服務程序中。但郵件接收是阻塞的,能夠設置接收超時的時間,不能在中斷服務程序裏面使用郵件接收。

3.當郵箱沒有郵件且超時時間不爲0 ,郵件的接收過程自動變爲阻塞方式。當郵箱滿了後,發送線程能夠選擇掛起等待或直接返回郵箱滿的錯誤碼。

4.消息隊列是一種異步的通訊方式,消息隊列裏面的消息老是遵循先進先出的原則。

5.能夠在線程或中斷服務程序裏面能夠給消息隊列發送消息,但不能在中斷服務程序裏面接收消息。

6.能夠往消息隊列裏面發送緊急消息,緊急消息會被放置到消息隊列的鏈表頭,會首先被等待的線程獲取。

7.信號跟信號量不一樣,不能混淆二者的概念,信號是軟件層面上的一種軟中斷方式。

8.線程不會用阻塞的方式等待信號的到來,由於線程自身也不知道這個信號(軟中斷)何時會到。

9.線程對信號的處理,能夠設置爲捕捉信號,忽略信號,使用默認方式處理信號。

原文連接:https://club.rt-thread.org/as...

相關文章
相關標籤/搜索