多線程是實(shí)時操作系統(tǒng)里面最重要的知識點(diǎn)之一,要學(xué)習(xí)RTOS,多線程是必須(沒錯,是必須)要熟練掌握的內(nèi)容,只有熟練掌握多線程的使用,才能在平時的項(xiàng)目工作里面用好實(shí)時操作系統(tǒng)。
關(guān)于多線程的使用和管理,RT-Thread官方提供了比較豐富的文檔作為參考,具體內(nèi)容可以查看以下鏈接:
https://www.rt-thread.org/document/site/programming-manual/thread/thread/
本文是對RT-Thread多線程學(xué)習(xí)后的總結(jié),并嘗試從如圖所示的以下幾個方面進(jìn)行總結(jié)。
什么是多線程?
在單片機(jī)上學(xué)習(xí)RT-Thread的多線程之前,要先把“進(jìn)程”這個概念先放一邊,因?yàn)閱纹瑱C(jī)是沒有多進(jìn)程概念的。單片機(jī)運(yùn)行操作系統(tǒng),不管多少個任務(wù),他們都是多個(或單個)線程之間進(jìn)行處理這些任務(wù),單片機(jī)一般不涉及多進(jìn)程。
什么是多線程?在哪些情況下要用到多線程?先來舉一個音樂播放器的例子,這個音樂播放器要做以下這些基本的工作:讀取音樂文件并播放、讀取歌詞并顯示、讀取MV文件并播放。
如果這三個基本的工作不用多線程來完成,單片機(jī)使用裸機(jī)的方式去做這三個工作的話,必然會造成音樂播放卡頓,歌詞顯示不同步,MV視頻播放與音樂不同步。
因?yàn)閱纹瑱C(jī)做這三件事情的時候,是Step by Step的,必須完成一件事情之后,再去做下一件事情,這三件事情是有先后順序的,并且不斷循環(huán)重復(fù),如下圖所示。
而如果采用多線程這種方式來完成這個工作,這個過程就變得相對簡單了,比如針對音樂播放器這個場景,可以設(shè)計(jì)這幾個線程來處理:音樂文件讀取線程,歌詞文件讀取線程,MV文件讀取線程,音視頻和歌詞顯示線程。
(此處只為舉例描述多線程的概念,不考慮音視頻編解碼的復(fù)雜過程,不考慮線程同步,實(shí)際上音樂播放器的實(shí)現(xiàn)比此處描述更復(fù)雜)
音樂文件讀取線程只負(fù)責(zé)從磁盤讀取音樂文件,歌詞文件讀取線程和MV文件讀取線程也是同樣的道理,它們只做文件讀取工作,而音視頻和歌詞顯示線程,是負(fù)責(zé)把讀取到的數(shù)據(jù)進(jìn)行顯示。這幾個線程的工作過程,如下圖所示。
如上圖所示,這幾個任務(wù)看上去是“同時”進(jìn)行的,每個任務(wù)都只完成自己的事情,通過多線程,就可以把原本串行完成的任務(wù)改為并行完成,大大提高了工作效率。
所以,通俗地對多線程進(jìn)行理解,就是把一個比較大型的任務(wù),拆分為多個小型的任務(wù),然后通過合理的調(diào)度方式,讓這幾個小型的任務(wù)“同時”運(yùn)行,當(dāng)這幾個小型任務(wù)完成后,大型的任務(wù)也隨之完成,這樣可以大大提高任務(wù)的完成效率。
多線程的幾種狀態(tài)
對于運(yùn)行RT-Thread操作系統(tǒng),線程都處于以下五種狀態(tài)的其中一種(初始狀態(tài)、就緒狀態(tài)、運(yùn)行狀態(tài)、掛起狀態(tài)、關(guān)閉狀態(tài)),通過調(diào)用操作系統(tǒng)提供的接口函數(shù),可以讓線程在這五種狀態(tài)中進(jìn)行來回切換。
關(guān)于這五種線程狀態(tài)的描述,如下表所示:
多線程的API函數(shù)
如上圖的狀態(tài)機(jī)所示,多線程可以通過調(diào)用系統(tǒng)提供的函數(shù)接口,在多個狀態(tài)之間進(jìn)行切換。這些API函數(shù)在官方提供的參考文檔里面都有詳細(xì)的說明描述,以下列舉一些比較常用的函數(shù)接口。
上下滑動查看 API 函數(shù)
多線程的應(yīng)用示例
多線程的應(yīng)用示例,主要是為了驗(yàn)證以上的多線程API接口函數(shù),并且通過實(shí)驗(yàn)現(xiàn)象觀察多線程的運(yùn)行情況,主要有以下三個示例:
示例源碼下載鏈接:
https://github.com/embediot/rtthread_study_notes
1、線程動態(tài)創(chuàng)建與靜態(tài)創(chuàng)建、線程退出示例。
這個示例主要是通過動態(tài)方式創(chuàng)建線程1,,通過靜態(tài)方式創(chuàng)建線程2,線程1的優(yōu)先級比線程2的優(yōu)先級低,因此可以被線程2搶占。線程2運(yùn)行10次后就會主動退出,初始化代碼如下圖所示。
2、相同優(yōu)先級線程的時間片輪轉(zhuǎn)調(diào)度示例。
這個示例主要是通過動態(tài)方式創(chuàng)建線程1和線程2,這兩個線程都是相同的優(yōu)先級,并且共用一個線程入口函數(shù),主要是通過傳入不同的線程參數(shù)以區(qū)分線程1和線程2。線程2運(yùn)行所占用的時間片比線程1要少,因此線程2運(yùn)行的時間比較短,初始化代碼如下圖所示。
3、線程調(diào)度器的鉤子函數(shù)使用示例。
這個示例主要測試了線程在進(jìn)行調(diào)度時,關(guān)于鉤子函數(shù)的調(diào)用情況。通過線程調(diào)度器的鉤子函數(shù),打印出線程間的切換信息,初始化的代碼如下圖所示。
多線程應(yīng)用的注意事項(xiàng)
在使用RT-Thread實(shí)時操作系統(tǒng)進(jìn)行多線程應(yīng)用開發(fā)的時候,應(yīng)該要注意以下事項(xiàng):
1、RT-Thread的線程調(diào)度器是搶占式的,也就是能夠保證就緒隊(duì)列里面,最高優(yōu)先級的任務(wù)總能獲得CPU的使用權(quán),在任務(wù)設(shè)計(jì)的時候,要充分考慮好任務(wù)的優(yōu)先級。
2、在硬件中斷服務(wù)程序運(yùn)行期間,如果有高優(yōu)先級的任務(wù)就緒,那么被中斷的低優(yōu)先級任務(wù)將被掛起,高優(yōu)先級的任務(wù)將會獲得CPU的使用權(quán)。
3、每個線程都有獨(dú)立的線程棧,用來保存線程調(diào)度時上下文的信息,因此在創(chuàng)建線程分配??臻g的時候,要充分考慮棧的大小。
4、在線程的循環(huán)體里面,應(yīng)該要設(shè)置某些條件,在必要的時候主動讓出CPU的使用權(quán),特別對于高優(yōu)先級的線程,如果程序里面有死循環(huán)操作而又不主動讓出CPU使用權(quán),那么這個線程將會一直占用CPU,并且低優(yōu)先級的線程永遠(yuǎn)不會被調(diào)度執(zhí)行。
5、對于沒有一直循環(huán)執(zhí)行的線程,線程執(zhí)行完畢后,資源的回收情況實(shí)際上是在空閑線程里面進(jìn)行的,線程變?yōu)殛P(guān)閉狀態(tài)后,不代表資源馬上被回收。
6、系統(tǒng)空閑線程是最低優(yōu)先級且永遠(yuǎn)為就緒狀態(tài)的,空閑線程是一個死循環(huán),永遠(yuǎn)不會被掛起,但可以被其他高優(yōu)先級任務(wù)搶占,空閑線程主要執(zhí)行僵尸線程的資源回收工作。
7、空閑線程也可以設(shè)置鉤子函數(shù),用來進(jìn)行功耗管理,看門狗喂狗等工作。
8、通過動態(tài)方式創(chuàng)建的線程,需要設(shè)置好系統(tǒng)堆內(nèi)存的大小,而通過靜態(tài)方式創(chuàng)建的線程,線程棧和線程句柄在程序編譯的時候就已經(jīng)確定,不能被動態(tài)分配,也不能被釋放。
9、大多數(shù)線程都是在不斷循環(huán)執(zhí)行的,無需進(jìn)行刪除,一般不推薦主動刪除線程。線程運(yùn)行完畢后,系統(tǒng)調(diào)度器將會自動把線程加入僵尸隊(duì)列,資源回收工作將在空閑線程里面進(jìn)行。
責(zé)任編輯:lq
-
操作系統(tǒng)
+關(guān)注
關(guān)注
37文章
6862瀏覽量
123506 -
多線程
+關(guān)注
關(guān)注
0文章
278瀏覽量
20035 -
RT-Thread
+關(guān)注
關(guān)注
31文章
1301瀏覽量
40265
原文標(biāo)題:【學(xué)習(xí)筆記】RT-Thread 多線程學(xué)習(xí)總結(jié)
文章出處:【微信號:RTThread,微信公眾號:RTThread物聯(lián)網(wǎng)操作系統(tǒng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論