一、前言
從這一講開(kāi)始,我們進(jìn)入RT-thread內(nèi)核的學(xué)習(xí),這是操作系統(tǒng)和裸機(jī)的區(qū)別,也是操作系統(tǒng)的核心所在,關(guān)于內(nèi)核的基礎(chǔ)我就不介紹了,大家可以先去官網(wǎng)上了解一下什么是內(nèi)核。我這一講重點(diǎn)講解內(nèi)核的線程管理,關(guān)于內(nèi)核的其他內(nèi)容我后續(xù)會(huì)接著講。
事先說(shuō)明,本人也是剛接觸RT-thread不久,有些理解可能還比較顯淺,如果大家發(fā)現(xiàn)有什么錯(cuò)誤,請(qǐng)一定要指正,謝謝?。?!
然后就是文中有一些概念的定義和描述我是直接在官網(wǎng)上面抄過(guò)來(lái)的,因?yàn)槲矣X(jué)得官網(wǎng)上有些有些概念寫(xiě)的非常清晰明了,我自己去描述的話(huà)可能還會(huì)帶大家繞彎。
內(nèi)核基礎(chǔ)知識(shí):https://www.rt-thread.org/document/site/programming-manual/basic/basic/#
源碼鏈接
我發(fā)布的所有關(guān)于RT-thread的教程源代碼都在下面這個(gè)鏈接里面,隨著我教程的更新,新的代碼也會(huì)加入進(jìn)去。
教程源碼下載鏈接:https://pan.baidu.com/s/1N2D8dM31deKIqNqaIQfPiA
提取碼:7nsx
二、線程介紹
什么是線程
玩過(guò)單片機(jī)的同學(xué)應(yīng)該都知道,裸機(jī)程序在運(yùn)行完啟動(dòng)程序之后總是會(huì)從main函數(shù)開(kāi)始執(zhí)行用戶(hù)的程序。
我們這里假設(shè)有這么一份代碼,main函數(shù)里面有一個(gè)while循環(huán),里面調(diào)用了兩個(gè)函數(shù),函數(shù)A和函數(shù)B,main函數(shù)以外有一個(gè)中斷服務(wù)函數(shù)C。函數(shù)A是一個(gè)溫濕度采集函數(shù),函數(shù)B是LCD顯示函數(shù),顯示溫濕度的值,函數(shù)C是按鍵的中斷服務(wù)函數(shù),按鍵按下的時(shí)候停止溫濕度的采集。其實(shí)這三個(gè)函數(shù)我們就可以把它看作是三個(gè)線程,函數(shù)A和函數(shù)B是優(yōu)先級(jí)等同的兩個(gè)線程,正常運(yùn)行的時(shí)候,先運(yùn)行函數(shù)A,再運(yùn)行函數(shù)B,如此循環(huán),如果中斷觸發(fā)了,優(yōu)先級(jí)更高的函數(shù)C就會(huì)被優(yōu)先處理,整個(gè)程序運(yùn)行的流程其實(shí)就體現(xiàn)了單線程的線程管理和調(diào)度。
RT-thread的線程管理其實(shí)也是一樣的,線程是實(shí)現(xiàn)任務(wù)的載體,它是 RT-Thread 中最基本的調(diào)度單位,它描述了一個(gè)任務(wù)執(zhí)行的運(yùn)行環(huán)境,也描述了這個(gè)任務(wù)所處的優(yōu)先等級(jí),重要的任務(wù)可設(shè)置相對(duì)較高的優(yōu)先級(jí),非重要的任務(wù)可以設(shè)置較低的優(yōu)先級(jí),不同的任務(wù)還可以設(shè)置相同的優(yōu)先級(jí),輪流運(yùn)行。
線程的優(yōu)點(diǎn)
到這里,不知道大家有沒(méi)有一個(gè)疑惑,既然裸機(jī)程序函數(shù)的調(diào)度相當(dāng)于線程的調(diào)度,那為什么裸機(jī)的程序沒(méi)有線程這個(gè)概念呢?它們運(yùn)行的時(shí)候本質(zhì)都是一樣的呀,為什么很多人都說(shuō)操作系統(tǒng)的運(yùn)行效率更高呢?
我的理解是這樣的,裸機(jī)的函數(shù)調(diào)度的方式比較單一,就是不斷循環(huán)的調(diào)用這些函數(shù),函數(shù)的優(yōu)先級(jí)也不多。而操作系統(tǒng)的每一個(gè)線程都可以設(shè)置優(yōu)先級(jí),我們可以根據(jù)需要把重要的任務(wù)分配更高的優(yōu)先級(jí),從而使得線程的調(diào)度不是單一的死循環(huán)調(diào)度,在多線程調(diào)度的時(shí)候,操作系統(tǒng)的存在使得代碼的運(yùn)行效率大大的提高。
舉個(gè)例子,有一個(gè)函數(shù)A,里面有多個(gè)延時(shí)函數(shù),有幾ms的,也有幾十ms的,那么這個(gè)函數(shù)A如果在裸機(jī)的程序里面跑的時(shí)候必須規(guī)規(guī)矩矩等待延時(shí)結(jié)束再往下跑,而如果是在RT-thread操作系統(tǒng)里面跑的話(huà),當(dāng)執(zhí)行延時(shí)函數(shù)的時(shí)候,函數(shù)A的線程會(huì)被掛起,然后執(zhí)行其他線程里面優(yōu)先級(jí)最高的,等延時(shí)時(shí)間到了再返回,繼續(xù)在函數(shù)A往下跑。這樣一來(lái),當(dāng)代碼量比較龐大,函數(shù)關(guān)系復(fù)雜的時(shí)候,操作系統(tǒng)的優(yōu)勢(shì)就體現(xiàn)出來(lái)了。
線程棧
RT-Thread 線程具有獨(dú)立的棧,當(dāng)進(jìn)行線程切換時(shí),會(huì)將當(dāng)前線程的上下文存在棧中,當(dāng)線程要恢復(fù)運(yùn)行時(shí),再?gòu)臈V凶x取上下文信息,進(jìn)行恢復(fù)。
線程棧還用來(lái)存放函數(shù)中的局部變量:函數(shù)中的局部變量從線程棧空間中申請(qǐng);函數(shù)中局部變量初始時(shí)從寄存器中分配(ARM 架構(gòu)),當(dāng)這個(gè)函數(shù)再調(diào)用另一個(gè)函數(shù)時(shí),這些局部變量將放入棧中。
線程的類(lèi)型
RT-thread的線程有兩種類(lèi)型,分別是系統(tǒng)線程和用戶(hù)線程.
系統(tǒng)線程是由 RT-Thread 內(nèi)核創(chuàng)建的線程
用戶(hù)線程是由我們自己創(chuàng)建的線程,當(dāng)創(chuàng)建的線程被啟動(dòng)之后就會(huì)加入任務(wù)的調(diào)度。
線程的狀態(tài)
線程在運(yùn)行的時(shí)候有多種狀態(tài),具體如下表所示:
線程的優(yōu)先級(jí)
因?yàn)镽T-Thread 的線程調(diào)度器是搶占式的,所以線程的優(yōu)先級(jí)非常重要。RT-Thread在運(yùn)行的時(shí)候會(huì)先從就緒線程列表中查找最高優(yōu)先級(jí)的線程運(yùn)行,運(yùn)行完了或者需要延時(shí)的時(shí)候會(huì)讓出cpu的使用權(quán),讓就緒線程列表中該線程以外的優(yōu)先級(jí)最高的線程運(yùn)行。
RT-Thread 最大支持 256 個(gè)線程優(yōu)先級(jí) (0~255),數(shù)值越小的優(yōu)先級(jí)越高,0 為最高優(yōu)先級(jí)。在一些資源比較緊張的系統(tǒng)中,可以根據(jù)實(shí)際情況選擇只支持 8 個(gè)或 32 個(gè)優(yōu)先級(jí)的系統(tǒng)配置;對(duì)于 ARM Cortex-M 系列,普遍采用 32 個(gè)優(yōu)先級(jí)。最低優(yōu)先級(jí)默認(rèn)分配給空閑線程使用,用戶(hù)一般不使用。在系統(tǒng)中,當(dāng)有比當(dāng)前線程優(yōu)先級(jí)更高的線程就緒時(shí),當(dāng)前線程將立刻被換出,高優(yōu)先級(jí)線程搶占處理器運(yùn)行。
線程時(shí)間片
每個(gè)線程都有時(shí)間片這個(gè)參數(shù),但時(shí)間片僅對(duì)優(yōu)先級(jí)相同的就緒態(tài)線程有效。系統(tǒng)對(duì)優(yōu)先級(jí)相同的就緒態(tài)線程采用時(shí)間片輪轉(zhuǎn)的調(diào)度方式進(jìn)行調(diào)度時(shí),時(shí)間片起到約束線程單次運(yùn)行時(shí)長(zhǎng)的作用。
假設(shè)有 2 個(gè)優(yōu)先級(jí)相同的就緒態(tài)線程 A 與 B,A 線程的時(shí)間片設(shè)置為 10,B 線程的時(shí)間片設(shè)置為 5,那么當(dāng)系統(tǒng)中不存在比 A 優(yōu)先級(jí)高的就緒態(tài)線程時(shí),系統(tǒng)會(huì)在 A、B 線程間來(lái)回切換執(zhí)行,并且每次對(duì) A 線程執(zhí)行 10 個(gè)節(jié)拍的時(shí)長(zhǎng),對(duì) B 線程執(zhí)行 5 個(gè)節(jié)拍的時(shí)長(zhǎng)。
線程入口函數(shù)
我前面也說(shuō)過(guò)了,一個(gè)函數(shù)可以當(dāng)做是一個(gè)線程,在RT-Thread實(shí)際的操作中也是一樣,我們每創(chuàng)建一個(gè)線程,就必須創(chuàng)建一個(gè)線程的入口函數(shù)。
線程的入口函數(shù)一般有以下兩種代碼形式:
無(wú)限循環(huán)模式:
使用這種模式的時(shí)候需要注意的是,該線程一旦被運(yùn)行,就會(huì)導(dǎo)致優(yōu)先級(jí)比它低的線程一直不能夠被調(diào)度,因?yàn)檫@個(gè)線程本身是一個(gè)死循環(huán),它能夠一直在低優(yōu)先級(jí)線程前面被調(diào)度。
如果我們需要死循環(huán)但是又不需要一直死循環(huán)的時(shí)候,可以把這個(gè)線程配置較低的優(yōu)先級(jí),或者在函數(shù)里面調(diào)用延時(shí)函數(shù),從而使得該線程能夠讓出cpu使用權(quán)。
void thread_entry(void* paramenter)
{
while (1)
{
/* 等待事件的發(fā)生 */
/* 對(duì)事件進(jìn)行服務(wù)、進(jìn)行處理 */
}
}
順序執(zhí)行或有限次循環(huán)模式:
此類(lèi)線程不會(huì)循環(huán)或不會(huì)永久循環(huán),可謂是 “一次性” 線程,一定會(huì)被執(zhí)行完畢。而且在執(zhí)行完畢后,線程將被系統(tǒng)自動(dòng)刪除。比如下面這種簡(jiǎn)單的順序語(yǔ)句或者do whlie()、for()循環(huán)等。
static void thread_entry(void* parameter)
{
/* 處理事務(wù) #1 */
…
/* 處理事務(wù) #2 */
…
/* 處理事務(wù) #3 */
}
線程錯(cuò)誤碼
一個(gè)線程就是一個(gè)執(zhí)行場(chǎng)景,錯(cuò)誤碼是與執(zhí)行環(huán)境密切相關(guān)的,所以每個(gè)線程配備了一個(gè)變量用于保存錯(cuò)誤碼,線程的錯(cuò)誤碼有以下幾種。
#define RT_EOK 0 /* 無(wú)錯(cuò)誤 */
#define RT_ERROR 1 /* 普通錯(cuò)誤 */
#define RT_ETIMEOUT 2 /* 超時(shí)錯(cuò)誤 */
#define RT_EFULL 3 /* 資源已滿(mǎn) */
#define RT_EEMPTY 4 /* 無(wú)資源 */
#define RT_ENOMEM 5 /* 無(wú)內(nèi)存 */
#define RT_ENOSYS 6 /* 系統(tǒng)不支持 */
#define RT_EBUSY 7 /* 系統(tǒng)忙 */
#define RT_EIO 8 /* IO 錯(cuò)誤 */
#define RT_EINTR 9 /* 中斷系統(tǒng)調(diào)用 */
#define RT_EINVAL 10 /* 非法參數(shù) */
線程狀態(tài)切換
RT-Thread 提供一系列的操作系統(tǒng)調(diào)用接口,使得線程的狀態(tài)在這五個(gè)狀態(tài)之間來(lái)回切換。幾種狀態(tài)間的轉(zhuǎn)換關(guān)系如下圖所示:
1:線程通過(guò)調(diào)用函數(shù) rt_thread_create/init() 進(jìn)入到初始狀態(tài)(RT_THREAD_INIT)。
2:初始狀態(tài)的線程通過(guò)調(diào)用函數(shù) rt_thread_startup() 進(jìn)入到就緒狀態(tài)(RT_THREAD_READY);
3:就緒狀態(tài)的線程被調(diào)度器調(diào)度后進(jìn)入運(yùn)行狀態(tài)(RT_THREAD_RUNNING)。
4:當(dāng)處于運(yùn)行狀態(tài)的線程調(diào)用 rt_thread_delay(),rt_sem_take(),rt_mutex_take(),rt_mb_recv() 等函數(shù)或者獲取不到資源時(shí),將進(jìn)入到掛起狀態(tài)(RT_THREAD_SUSPEND)。
5:掛起狀態(tài)的線程,如果等待超時(shí)依然未能獲得資源或由于其他線程釋放了資源,那么它將返回到就緒狀態(tài)。
6:掛起狀態(tài)的線程,如果調(diào)用 rt_thread_delete/detach() 函數(shù),將更改為關(guān)閉狀態(tài)(RT_THREAD_CLOSE)。
7:運(yùn)行狀態(tài)的線程,如果運(yùn)行結(jié)束,就會(huì)在線程的最后部分執(zhí)行 rt_thread_exit() 函數(shù),將狀態(tài)更改為關(guān)閉狀態(tài)。
#1:線程通過(guò)調(diào)用函數(shù) rt_thread_create/init() 進(jìn)入到初始狀態(tài)(RT_THREAD_INIT)。
2:初始狀態(tài)的線程通過(guò)調(diào)用函數(shù) rt_thread_startup() 進(jìn)入到就緒狀態(tài)(RT_THREAD_READY);
3:就緒狀態(tài)的線程被調(diào)度器調(diào)度后進(jìn)入運(yùn)行狀態(tài)(RT_THREAD_RUNNING)。
4:當(dāng)處于運(yùn)行狀態(tài)的線程調(diào)用 rt_thread_delay(),rt_sem_take(),rt_mutex_take(),rt_mb_recv() 等函數(shù)或者獲取不到資源時(shí),將進(jìn)入到掛起狀態(tài)(RT_THREAD_SUSPEND)。
5:掛起狀態(tài)的線程,如果等待超時(shí)依然未能獲得資源或由于其他線程釋放了資源,那么它將返回到就緒狀態(tài)。
6:掛起狀態(tài)的線程,如果調(diào)用 rt_thread_delete/detach() 函數(shù),將更改為關(guān)閉狀態(tài)(RT_THREAD_CLOSE)。
7:運(yùn)行狀態(tài)的線程,如果運(yùn)行結(jié)束,就會(huì)在線程的最后部分執(zhí)行 rt_thread_exit() 函數(shù),將狀態(tài)更改為關(guān)閉狀態(tài)。
系統(tǒng)線程
系統(tǒng)線程是指由系統(tǒng)創(chuàng)建的線程,用戶(hù)線程是由用戶(hù)程序調(diào)用線程管理接口創(chuàng)建的線程,在 RT-Thread 內(nèi)核中的系統(tǒng)線程有空閑線程和主線程。
空閑線程
空閑線程是系統(tǒng)創(chuàng)建的最低優(yōu)先級(jí)的線程,線程狀態(tài)永遠(yuǎn)為就緒態(tài)。當(dāng)系統(tǒng)中無(wú)其他就緒線程存在時(shí),調(diào)度器將調(diào)度到空閑線程,它通常是一個(gè)死循環(huán),且永遠(yuǎn)不能被掛起。關(guān)于空閑線程在 RT-Thread的特殊用途,我在以后的教程里面再說(shuō)。
主線程
在系統(tǒng)啟動(dòng)時(shí),系統(tǒng)會(huì)創(chuàng)建 main 線程,用戶(hù)可以在 main() 函數(shù)里添加自己的應(yīng)用程序初始化代碼。但是在實(shí)際的項(xiàng)目運(yùn)用中,為了方便管理每一個(gè)功能模塊,我建議大家最好不要在main() 函數(shù)里面寫(xiě)應(yīng)用的代碼,原因我這里不多說(shuō)了,在后續(xù)的教程中我會(huì)一一講解。
三、編程講解
我這里只介紹線程最常用的最基本的用法,如果你們還想學(xué)習(xí)進(jìn)階的用法,可以到官網(wǎng)上查閱相關(guān)的資料。
第一步: 創(chuàng)建/初始化線程
創(chuàng)建線程創(chuàng)建的是一個(gè)動(dòng)態(tài)線程,即線程??臻g的內(nèi)存是由系統(tǒng)自由分配的。初始化線程創(chuàng)建的是一個(gè)靜態(tài)線程,線程??臻g的內(nèi)存由用戶(hù)自己去指定。
創(chuàng)建線程函數(shù)如下圖所示:
系統(tǒng)線程
系統(tǒng)線程是指由系統(tǒng)創(chuàng)建的線程,用戶(hù)線程是由用戶(hù)程序調(diào)用線程管理接口創(chuàng)建的線程,在 RT-Thread 內(nèi)核中的系統(tǒng)線程有空閑線程和主線程。
空閑線程:
空閑線程是系統(tǒng)創(chuàng)建的最低優(yōu)先級(jí)的線程,線程狀態(tài)永遠(yuǎn)為就緒態(tài)。當(dāng)系統(tǒng)中無(wú)其他就緒線程存在時(shí),調(diào)度器將調(diào)度到空閑線程,它通常是一個(gè)死循環(huán),且永遠(yuǎn)不能被掛起。關(guān)于空閑線程在 RT-Thread的特殊用途,我在以后的教程里面再說(shuō)。
主線程:
在系統(tǒng)啟動(dòng)時(shí),系統(tǒng)會(huì)創(chuàng)建 main 線程,用戶(hù)可以在 main() 函數(shù)里添加自己的應(yīng)用程序初始化代碼。但是在實(shí)際的項(xiàng)目運(yùn)用中,為了方便管理每一個(gè)功能模塊,我建議大家最好不要在main() 函數(shù)里面寫(xiě)應(yīng)用的代碼,原因我這里不多說(shuō)了,在后續(xù)的教程中我會(huì)一一講解。
三、編程講解
我這里只介紹線程最常用的最基本的用法,如果你們還想學(xué)習(xí)進(jìn)階的用法,可以到官網(wǎng)上查閱相關(guān)的資料。
第一步: 創(chuàng)建/初始化線程
創(chuàng)建線程創(chuàng)建的是一個(gè)動(dòng)態(tài)線程,即線程棧空間的內(nèi)存是由系統(tǒng)自由分配的。初始化線程創(chuàng)建的是一個(gè)靜態(tài)線程,線程棧空間的內(nèi)存由用戶(hù)自己去指定。
創(chuàng)建線程函數(shù)如下圖所示:
編程示例:
/* 創(chuàng)建線程:led0 */
rt_thread_t thread1 = rt_thread_create("led0", //線程名稱(chēng)
led0_entry, //線程入口函數(shù)
RT_NULL, //線程入口函數(shù)參數(shù)
1024, //線程棧大小
25, //優(yōu)先級(jí)
10); //時(shí)間片
初始化線程函數(shù)的定義如下圖所示:
編程示例:
/* 靜態(tài)線程參數(shù)定義 */
ALIGN(RT_ALIGN_SIZE)
static char led1_stack[1024]; //線程棧內(nèi)存空間
static struct rt_thread led1; //線程句柄
/* 創(chuàng)建線程:led1 */
rt_thread_init(&led1, //線程句柄
"led1", //線程名稱(chēng)
led1_entry, //線程入口函數(shù)
RT_NULL, //線程入口函數(shù)參數(shù)
&led1_stack[0], //線程棧起始地址
sizeof(led1_stack), //線程棧大小
THREAD_PRIORITY - 1, //優(yōu)先級(jí)
THREAD_TIMESLICE); //時(shí)間片
第二步:?jiǎn)?dòng)線程
編程示例:
/* 創(chuàng)建線程:led0 */
rt_thread_t thread1 = rt_thread_create("led0", //線程名稱(chēng)
led0_entry, //線程入口函數(shù)
RT_NULL, //線程入口函數(shù)參數(shù)
1024, //線程棧大小
25, //優(yōu)先級(jí)
10); //時(shí)間片
/* 如果線程創(chuàng)建成功,啟動(dòng)這個(gè)線程 */
if (thread1 != RT_NULL)
{
rt_thread_startup(thread1);
}
第三步:編寫(xiě)線程入口函數(shù)
線程的入口函數(shù)是由用戶(hù)自己定義的,我們只要保證入口函數(shù)的函數(shù)名和創(chuàng)建線程時(shí)所用的函數(shù)名一致即可。
編程示例:
```c
void led0_entry(void *parameter)
{
while(1)
{
rt_pin_write(LED0_PIN, PIN_LOW);
rt_thread_mdelay(1000);
rt_pin_write(LED0_PIN, PIN_HIGH);
rt_thread_mdelay(1000);
}
}
四、項(xiàng)目實(shí)戰(zhàn)
我這里在main函數(shù)里面創(chuàng)建了一個(gè)動(dòng)態(tài)和一個(gè)靜態(tài)線程,分別讓led0和led1閃爍。其實(shí)在實(shí)際的項(xiàng)目應(yīng)用中,一般不會(huì)在main函數(shù)加入很多應(yīng)用的代碼,而是用命令的方式把函數(shù)加入應(yīng)用列表,這個(gè)我在下一講才會(huì)介紹,所以這里就先這樣寫(xiě)吧。
代碼如下:
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#define LED0_PIN GET_PIN(F, 9)
#define LED1_PIN GET_PIN(F, 10)
#define THREAD_PRIORITY 25 //線程優(yōu)先級(jí)
#define THREAD_TIMESLICE 5 //線程時(shí)間片
/* 靜態(tài)線程參數(shù)定義 */
ALIGN(RT_ALIGN_SIZE)
static char led1_stack[1024]; //線程棧內(nèi)存空間
static struct rt_thread led1; //線程句柄
/* led0線程入口函數(shù) */
void led0_entry(void *parameter)
{
rt_pin_write(LED0_PIN, PIN_LOW);
rt_thread_mdelay(1000);
rt_pin_write(LED0_PIN, PIN_HIGH);
rt_thread_mdelay(1000);
}
/* led1線程入口函數(shù) */
void led1_entry(void *parameter)
{
rt_pin_write(LED1_PIN, PIN_LOW);
rt_thread_mdelay(300);
rt_pin_write(LED1_PIN, PIN_HIGH);
rt_thread_mdelay(300);
}
int main(void)
{
int i = 0;
/* 把LED引腳設(shè)置為輸出 */
rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT);
/* 創(chuàng)建線程:led0 */
rt_thread_t thread1 = rt_thread_create("led0", //線程名稱(chēng)
led0_entry, //線程入口函數(shù)
RT_NULL, //線程入口函數(shù)參數(shù)
1024, //線程棧大小
25, //優(yōu)先級(jí)
10); //時(shí)間片
/* 如果線程創(chuàng)建成功,啟動(dòng)這個(gè)線程 */
if (thread1 != RT_NULL)
{
rt_thread_startup(thread1);
}
/* 創(chuàng)建線程:led1 */
rt_thread_init(&led1, //線程句柄
"led1", //線程名稱(chēng)
led1_entry, //線程入口函數(shù)
RT_NULL, //線程入口函數(shù)參數(shù)
&led1_stack[0], //線程棧起始地址
sizeof(led1_stack), //線程棧大小
THREAD_PRIORITY - 1, //優(yōu)先級(jí)
THREAD_TIMESLICE); //時(shí)間片
rt_thread_startup(&led1);
while (1)
{
rt_thread_mdelay(100);
}
}
五、結(jié)束語(yǔ)
線程在RT-thread項(xiàng)目的實(shí)戰(zhàn)中運(yùn)用非常廣泛,因此,希望大家好好看一下線程相關(guān)的內(nèi)容,了解更多線程的用法。
好了,關(guān)于線程管理的編程講解就到這里,如果還有什么問(wèn)題可以私信給我。如果需要本文對(duì)應(yīng)的源碼的話(huà)可以在博文前言部分的鏈接下載。
如果覺(jué)得這篇文章對(duì)你有用,點(diǎn)贊+關(guān)注支持一下博主唄。
后續(xù)我會(huì)繼續(xù)更新RT-thread入門(mén)教程系列,如果感興趣的同學(xué)可以關(guān)注一下博主,謝謝!