RT-Thread學習筆記
- 線程間同步
- 信號量
- 信號量的使用和管理
- 動態創建信號量
- 靜態創建信號量
- 獲取信號量
- 信號量同步實列
- 互斥量
- 互斥量的使用和管理
- 互斥量動態創建
- 互斥量靜態創建
- 互斥量獲取和釋放
- 互斥量實例
- 事件集
- 事件集的使用和管理
- 動態創建事件集
- 靜態初始化事件集
- 發送和接收事件
- 事件集實現線程同步實例
線程間同步
線程間同步的作用:多個執行單元(線程、中斷)同時執行臨界區,操作臨界資源,會導致竟態產生,線程間同步確保多個線程在訪問共享資源時,能夠按照正確的順序執行,避免數據沖突和邏輯錯誤。
臨界區(Critical Section)是指一個程序中訪問共享資源(如全局變量、內存、文件、硬件設備)的一段代碼區域,在這段代碼執行過程中,必須保證同一時刻最多只能有一個線程進入執行,以避免數據競爭和不一致問題。
? 具體作用如下:
防止數據競爭(競態條件)
多個線程同時修改同一份數據時,如果沒有同步機制,就可能出現數據錯誤或程序行為異常。
保證數據一致性
同步機制(如互斥鎖、信號量等)可以確保同一時間只有一個線程訪問臨界資源,保證操作的原子性。
協調線程執行順序
某些任務必須在另一個線程完成之后才能執行,比如:先讀取文件,后處理數據。這種依賴關系就需要同步手段來控制執行順序。
提高程序穩定性與可靠性
通過同步機制可以避免死鎖、資源沖突等常見問題,使并發程序更健壯。
🔧RT-Thread OS提供了如下幾種同步互斥機制:
互斥鎖(mutex)
信號量(semaphore)
事件集(event)
信號量
信號量是一種輕型的用于解決線程間同步問題的內核對象,即信號量也是通過結構體定義描述的,線程可以獲取或釋放它,從而達到同步或互斥的目的。每個信號量對象都有一個信號量值和一個線程等待隊列,信號量的值對應了信號量對象的實例數目、資源數目,假如信號量值為 5,則表示共有 5 個信號量實例(資源)可以被使用,當信號量實例數目為零時,再申請該信號量的線程就會被掛起在該信號量的等待隊列上,等待可用的信號量實例(資源)
例如初始化信號量值為 1 ,當線程執行到臨界區時要獲取信號量,此時信號量值為 1,因此該線程可以獲取到信號量,當信號量被獲取一次后,信號量值會 減1,此時信號量值為0,當下一個線程需要獲取信號量時,該線程就會被掛起,放到一個等待隊列中去,等到信號量資源大于0時,回到對應的等待隊列把掛起的線程喚醒,即將線程從掛起態切換為就緒態
信號量結構體
該結構體里有信號量的value值,還有一個預留參數,為后期的擴展使用
struct rt_semaphore
{struct rt_ipc_object parent; /**< inherit from ipc_object */rt_uint16_t value; /**< value of semaphore. */rt_uint16_t reserved; /**< reserved field 預留*/
};
typedef struct rt_semaphore *rt_sem_t;
信號量的使用和管理
對一個信號量的操作包含:創建 / 初始化信號量、獲取信號量、釋放信號量、刪除 / 脫離信號量
首先要創建或初始化一個信號量,線程在臨界區前獲取信號量,在臨界區后釋放信號量
動態創建信號量
動態創建信號量函數
第一個參數是信號量的名字,第二個是信號量值,
rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag)#define RT_IPC_FLAG_FIFO 0x00 /**< FIFOed IPC. @ref IPC. */
#define RT_IPC_FLAG_PRIO 0x01 /**< PRIOed IPC. @ref IPC. */
? 1. RT_IPC_FLAG_FIFO(先進先出)
含義:按線程進入等待隊列的先后順序來喚醒(排隊公平)。
特點:誰先來誰先走,無視線程優先級。
應用場景:對響應時間要求不高,強調公平性,例如:簡單同步、非實時任務。
🧠 示例: 線程 A、B、C 優先級不同,但如果是 A 先等待信號量,那釋放信號量時 A 會先被喚醒,即使 B 優先級更高。
? 2. RT_IPC_FLAG_PRIO(優先級優先)
含義:按線程的優先級高低來決定喚醒順序。
特點:優先級高的線程先被喚醒。
應用場景:對實時性要求高的系統,優先響應高優先級任務。
🧠 示例: 線程 A(低優先級)先等待資源,線程 B(高優先級)后加入等待隊列;當資源釋放時,線程 B 會被優先喚醒。
系統不再使用信號量時,可通過刪除信號量以釋放系統資源,適用于動態創建的信號量。調用這個函數時,系統將刪除這個信號量。如果刪除該信號量時,有線程正在等待該信號量,那么刪除操作會先喚醒等待在該信號量上的線程(等待線程的返回值是-RT_ERROR),然后再釋放信號量的內存資源。
刪除信號量函數
rt_err_t rt_sem_delete(rt_sem_t sem)
創建演示
#include <rtthread.h>#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "errno.h"rt_sem_t sem1;int main(void)
{sem1 = rt_sem_create("sem1_demo", 1, RT_IPC_FLAG_FIFO);//先進先出if(sem1 == RT_NULL)//如果創建失敗{LOG_E("rt_sem_create failed...\n");return -ENOMEM;}LOG_D("rt_sem_create successed...\n");//如果創建成功return RT_EOK;
}
信號量創建成功,但是信號量的名字不是sem1_demo,少了一個字符,因為信號量名字最多8個字符
改短一點就可以了
靜態創建信號量
初始化信號量
第一個參數是rt_sem_t
類型的結構體指針,需要定義,第二個參數是信號量的名字,第三個參數是信號量值,第四個參數是flag,與動態創建一樣
rt_err_t rt_sem_init(rt_sem_t sem,const char *name,rt_uint32_t value,rt_uint8_t flag)
信號量脫離
脫離信號量就是讓信號量對象從內核對象管理器中脫離,適用于靜態初始化的信號量使用該函數后,內核先喚醒所有掛在該信號量等待隊列上的線程,然后將該信號量從內核對象管理器中脫離。原來掛起在信號量上的等待線程將獲得 - RT_ERROR 的返回值。
rt_err_t rt_sem_detach(rt_sem_t sem)
初始化演示
#include <rtthread.h>#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "errno.h"rt_sem_t sem1;struct rt_semaphore sem2;//定義一個信號量結構體用于初始化信號量int main(void)
{int ret = 0;//用于接收初始化的返回值sem1 = rt_sem_create("sem1", 1, RT_IPC_FLAG_FIFO);//先進先出if(sem1 == RT_NULL)//如果創建失敗{LOG_E("rt_sem_create failed...\n");return -ENOMEM;}LOG_D("rt_sem_create successed...\n");//如果創建成功ret = rt_sem_init(&sem2, "sem2", 5, RT_IPC_FLAG_FIFO);if(ret < 0)//如果初始化失敗{LOG_E("rt_sem_init failed...\n");return ret;}LOG_D("rt_sem_init successed...\n");//如果初始化成功return RT_EOK;
}
獲取信號量
假設有一個臨界資源是讓 i++,如果兩個線程都可以操作該臨界資源,即兩個線程都會讓 i++,這時候就會發生競爭,因此就需要使用信號量的獲取和釋放
獲取信號量函數
第一個參數是要獲取哪個信號量,第二個參數timeout決定了是否阻塞等待,RT_WAITING_FOREVER表示一直等待,直到有信號量,T_WAITING_NO表示不等待,直接返回
rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t timeout)#define RT_WAITING_FOREVER -1 /**< Block forever until get resource. */
#define RT_WAITING_NO 0 /**< Non-block. */
另一個獲取信號量的函數,等價于rt_sem_take(rt_sem_t sem, T_WAITING_NO)
,即不等待,直接返回
rt_err_t rt_sem_trytake(rt_sem_t sem)
釋放信號量函數
在操作完臨界資源后要去釋放信號量,不然其他線程無法獲取這個信號量
rt_err_t rt_sem_trytake(rt_sem_t sem)
信號量同步實列
信號量的同步是指通過信號量(semaphore)來協調多個線程或任務之間的執行時機,使它們按照正確的順序運行,從而實現“誰先做、誰后做”的同步控制關系。
#include <rtthread.h>#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "errno.h"rt_sem_t sem1;rt_thread_t th1,th2;int flag = 0;struct rt_semaphore sem2;//定義一個信號量結構體用于初始化信號量void th1_entry(void *parameter)//線程1入口函數
{while(1)//循環執行{rt_thread_mdelay(5000);//一開始處于阻塞狀態,會跳轉到線程2rt_sem_take(sem1, RT_WAITING_FOREVER );flag ++;if(flag == 100)flag = 0;rt_kprintf("th1_entry[%d]\n",flag);rt_sem_release(&sem2);}
}void th2_entry(void *parameter)//線程2入口函數
{while(1)//一開始跳轉到線程2不會立即執行線程2,因為信號量2的值為0,因此處于掛起狀態{rt_sem_take(&sem2, RT_WAITING_FOREVER);if(flag > 0)flag --;rt_kprintf("th2_entry[%d]\n",flag);rt_sem_release(sem1);rt_thread_mdelay(1000);}
}int main(void)
{int ret = 0;//用于接收初始化的返回值sem1 = rt_sem_create("sem1", 1, RT_IPC_FLAG_FIFO);//初始化信號量1的值為1if(sem1 == RT_NULL)//如果創建失敗{LOG_E("rt_sem_create failed...\n");return -ENOMEM;}LOG_D("rt_sem_create successed...\n");//如果創建成功ret = rt_sem_init(&sem2, "sem2", 0, RT_IPC_FLAG_FIFO);//初始化信號量2的值為0if(ret < 0)//如果初始化失敗{LOG_E("rt_sem_init failed...\n");return ret;}LOG_D("rt_sem_init successed...\n");//如果初始化成功th1 = rt_thread_create("th1", th1_entry, NULL, 512, 20, 5);if(th1 == RT_NULL){LOG_E("th1 rt_thread_create failed...\n");return -ENOMEM;}LOG_D("th1 rt_thread_create successed...\n");th2 = rt_thread_create("th2", th2_entry, NULL, 512, 20, 5);if(th2 == RT_NULL){LOG_E("th2 rt_thread_create failed...\n");return -ENOMEM;}LOG_D("th2 rt_thread_create successed...\n");rt_thread_startup(th1);rt_thread_startup(th2);return RT_EOK;
}
分別創建兩個線程,flag作為臨界區,兩個線程都會對flag進行操作,因此需要通過信號量防止競態的發生,在初始化時,信號量1的值為1,信號量2的值為2,線程th1獲取的是信號量1,釋放的是信號量2,線程2獲取的是信號量2,釋放的是信號量1
在一開始,線程1的入口函數先用一個延時阻塞,這時線程1被掛起,線程2被調度,但是此時的信號量2值為0,線程2獲取不到信號量,因此線程2被掛起,等到線程1的延時結束,又回到線程1,此時線程1獲取信號量1成功,執行相應的操作,然后釋放信號量2,信號量1的值從1變為0,信號量2的值從0變為1,線程1執行完,又輪到線程2執行,此時信號量2值為1,因此線程2能夠獲取信號量2,對應的操作能夠執行,線程2又釋放信號量1,此時信號量1的值又變為1,信號量2的值又變為0,依次循環,就實現了信號的同步操作
互斥量
互斥量體現的是排他性,也是解決多線程同時操作臨界區臨界資源導致的竟態的一種方法。(類似于特殊的信號量——二值信號量(只有 0 和 1 )),即一個線程在執行時,另一個線程不能執行,不強調順序
與信號量的區別:信號量可由不同線程釋放,互斥量只能由同一線程進行釋放。
互斥量的使用和管理
互斥量的操作包含:創建 / 初始化互斥量、獲取互斥量、釋放互斥量、刪除 / 脫離互斥量
互斥量動態創建
不再使用互斥量時,通過刪除互斥量以釋放系統資源,適用于動態創建的互斥量當刪除一個互斥量時,所有等待此互斥量的線程都將被喚醒,等待線程獲得的返回值是 - RT_ERROR
互斥量動態創建函數
rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag)#define RT_IPC_FLAG_FIFO 0x00 /**< FIFOed IPC. @ref IPC. */
#define RT_IPC_FLAG_PRIO 0x01 /**< PRIOed IPC. @ref IPC. */
互斥量刪除函數
rt_err_t rt_mutex_delete(rt_mutex_t mutex)
#include <rtthread.h>#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "errno.h"rt_mutex_t mutex1;int main(void)
{mutex1 = rt_mutex_create("mutex1", RT_IPC_FLAG_FIFO);if(mutex1 == RT_NULL){LOG_E("mutex1 rt_mutex_create failed...\n");return -ENOMEM;}LOG_D("mutex1 rt_mutex_create successed...\n");return RT_EOK;
}
互斥量靜態創建
使用該函數接口后,內核先喚醒所有掛在該互斥量上的線程(線程的返回值是 -RT_ERROR ) ,然后系統將該互斥量從內核對象管理器中脫離。
互斥量靜態創建函數
第一個參數也是要先定義一個結構體作為參數傳入
rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag)
互斥量脫離函數
rt_err_t rt_mutex_detach(rt_mutex_t mutex)
#include <rtthread.h>#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "errno.h"rt_mutex_t mutex1;struct rt_mutex mutex2;//定義一個結構體用于靜態創建互斥量傳參int main(void)
{int ret;//用于接收靜態創建的返回值//動態創建mutex1 = rt_mutex_create("mutex1", RT_IPC_FLAG_FIFO);if(mutex1 == RT_NULL){LOG_E("mutex1 rt_mutex_create failed...\n");return -ENOMEM;}LOG_D("mutex1 rt_mutex_create successed...\n");//靜態創建ret = rt_mutex_init(&mutex2, "mutex2", RT_IPC_FLAG_FIFO);if(ret < 0){LOG_E("mutex2 rt_mutex_init failed...\n");return ret;}LOG_D("mutex2 rt_mutex_init successed...\n");return RT_EOK;
}
互斥量獲取和釋放
互斥量獲取函數
rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout)#define RT_WAITING_FOREVER -1 /**< Block forever until get resource. */
#define RT_WAITING_NO 0 /**< Non-block. */
另一種獲取函數,等價于rt_mutex_take(rt_mutex_t mutex,RT_WAITING_NO)
rt_err_t rt_mutex_trytake(rt_mutex_t mutex)
互斥量釋放函數
rt_err_t rt_mutex_release(rt_mutex_t mutex)
獲取相當于上鎖,釋放相當于解鎖,上了鎖的要等到解鎖后才能使用,例如上廁所,有一個人進去了,上了鎖,下一個人要使用這個廁所的話一定要等到上一個人解鎖出來了,下一個人才能進入廁所,上鎖,且沒有順序,誰先來就誰去
上面的內容都是創建或初始化,沒有調用過刪除或這脫離,在這里調用一下,看看效果是怎么樣的,但是一般情況下一個線程或者信號量或互斥量創建之后,就會一直使用,因此不需要去刪除,但有時也會存在特殊情況
互斥量實例
沒有互斥量
#include <rtthread.h>#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "errno.h"rt_mutex_t mutex1;rt_thread_t th1,th2;int flag1 ,flag2;void th1_entry(void *parameter)//線程1入口函數
{while(1)//循環執行{flag1 ++;rt_thread_mdelay(1000);}
}void th2_entry(void *parameter)//線程2入口函數
{while(1){flag1 ++;flag2 ++; rt_kprintf("flag1:[%d] flag2:[%d]\n",flag1,flag2);rt_thread_mdelay(1000);}
}int main(void)
{//動態創建mutex1 = rt_mutex_create("mutex1", RT_IPC_FLAG_FIFO);if(mutex1 == RT_NULL){LOG_E("mutex1 rt_mutex_create failed...\n");return -ENOMEM;}LOG_D("mutex1 rt_mutex_create successed...\n");th1 = rt_thread_create("th1", th1_entry, NULL, 512, 20, 5);if(th1 == RT_NULL){LOG_E("th1 rt_thread_create failed...\n");return -ENOMEM;}LOG_D("th1 rt_thread_create successed...\n");th2 = rt_thread_create("th2", th2_entry, NULL, 512, 20, 5);if(th2 == RT_NULL){LOG_E("th2 rt_thread_create failed...\n");return -ENOMEM;}LOG_D("th2 rt_thread_create successed...\n");rt_thread_startup(th1);rt_thread_startup(th2);return RT_EOK;
}
上面的代碼定義了兩個變量,flag1和flag2作為臨界區,代碼原本的功能是讓flag1和flag2同步增加,應該兩個值是相等的,創建兩個線程同時操作這兩個變量,在沒有互斥量的情況下,會出現不同步和覆蓋的可能,導致兩個變量不一致、不連續,如下圖結果,flag1 != flag2,因此要加上互斥量進行保護
加上互斥量
#include <rtthread.h>#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "errno.h"rt_mutex_t mutex1;rt_thread_t th1,th2;int flag1 ,flag2;void th1_entry(void *parameter)//線程1入口函數
{while(1)//循環執行{rt_mutex_take(mutex1, RT_WAITING_FOREVER);//上鎖flag1 ++;rt_thread_mdelay(1000);flag2 ++;rt_mutex_release(mutex1);//解鎖}
}void th2_entry(void *parameter)//線程2入口函數
{while(1){rt_mutex_take(mutex1, RT_WAITING_FOREVER);//上鎖flag1 ++;flag2 ++;rt_mutex_release(mutex1);//解鎖rt_kprintf("flag1:[%d] flag2:[%d]\n",flag1,flag2);rt_thread_mdelay(1000);}
}int main(void)
{//動態創建mutex1 = rt_mutex_create("mutex1", RT_IPC_FLAG_FIFO);if(mutex1 == RT_NULL){LOG_E("mutex1 rt_mutex_create failed...\n");return -ENOMEM;}LOG_D("mutex1 rt_mutex_create successed...\n");th1 = rt_thread_create("th1", th1_entry, NULL, 512, 20, 5);if(th1 == RT_NULL){LOG_E("th1 rt_thread_create failed...\n");return -ENOMEM;}LOG_D("th1 rt_thread_create successed...\n");th2 = rt_thread_create("th2", th2_entry, NULL, 512, 20, 5);if(th2 == RT_NULL){LOG_E("th2 rt_thread_create failed...\n");return -ENOMEM;}LOG_D("th2 rt_thread_create successed...\n");rt_thread_startup(th1);rt_thread_startup(th2);return RT_EOK;
}
加上了互斥量對臨界區的保護,同一時間,只有一個線程能夠對臨界區進行操作,操作完成后解鎖了,下一個線程才能對臨界區進行操作,這樣兩個變量就能夠同步相等了
事件集
事件集也是線程間同步的機制之一,一個事件集可以包含多個事件,利用事件集可以完成一對多,多對多的線程間同步。
一個線程和多個事件的關系可設置為: 其中任意一個事件喚醒 線程,或幾個事件都到達后喚醒線程,多個事件集合可以用一個32bit無符號整型變量來表示,變量的每一位代表一個事件,線程通過"邏輯與"或"邏輯或"將一個或多個事件關聯起來,形成事件組合
RT-Thread 定義的事件集有以下特點:
- 事件只與線程相關,事件間相互獨立
- 事件僅用于同步,不提供數據傳輸功能
- 事件無排隊性,即多次向線程發送同一事件(如果線程還未來得及讀走),其效果等同于只發送一次
事件集的使用和管理
對一個事件集的操作包含:創建/初始化事件集、發送事件、接收事件、刪除/脫離事件集
動態創建事件集
動態創建事件集函數
函數的返回值是一個rt_event_t
數據類型的結構體,其中結構體中有一個rt_uint32_t
數據類型的 set 集合,這個集合里面每一位代表一個事件,因此一個事件集里最多支持32個事件
rt_event_t rt_event_create(const char *name, rt_uint8_t flag)#define RT_IPC_FLAG_FIFO 0x00 /**< FIFOed IPC. @ref IPC. */
#define RT_IPC_FLAG_PRIO 0x01 /**< PRIOed IPC. @ref IPC. */struct rt_event
{struct rt_ipc_object parent; /**< inherit from ipc_object */rt_uint32_t set; /**< event set */
};
typedef struct rt_event *rt_event_t;
刪除事件集函數
rt_err_t rt_event_delete(rt_event_t event)
靜態初始化事件集
靜態初始化事件集函數
要創建一個rt_event_t
類型的結構體指針作為參數傳入
rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag)
事件集脫離函數
rt_err_t rt_event_detach(rt_event_t event)
#include <rtthread.h>#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "errno.h"rt_event_t event1;struct rt_event event2;int main(void)
{int ret;//動態創建事件集event1 = rt_event_create("event1", RT_IPC_FLAG_FIFO);if(event1 == RT_NULL)//如果創建失敗{LOG_E("event1 rt_event_create failed...\n");return -ENOMEM;}LOG_D("event1 rt_event_create successed...\n");//如果創建成功//靜態初始化事件集ret = rt_event_init(&event2, "event2", RT_IPC_FLAG_FIFO);if(ret < 0)//如果初始化失敗{LOG_E("event2 rt_event_init failed...\n");return ret;}LOG_D("event2 rt_event_init successed...\n");//如果初始化成功return RT_EOK;
}
發送和接收事件
發送事件函數
第一個參數是事件集,第二個參數是事件集中的哪一個事件
rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set);
接收事件函數
第一個參數是事件集,第二個參數是事件集中的哪一個事件,第三個參數是與還是或還是清除了,即接收完這個位后要不要進行清除,第四個參數是超時時間,第五個參數用于存放接收到的值
rt_err_t rt_event_recv(rt_event_t event,rt_uint32_t set,rt_uint8_t opt,rt_int32_t timeout,rt_uint32_t *recved);
//opt參數
#define RT_EVENT_FLAG_AND 0x01 /**< logic and */
#define RT_EVENT_FLAG_OR 0x02 /**< logic or */
#define RT_EVENT_FLAG_CLEAR 0x04 /**< clear flag *///timeout參數
#define RT_WAITING_FOREVER -1 /**< Block forever until get resource. */
#define RT_WAITING_NO 0 /**< Non-block. */
事件集實現線程同步實例
#include <rtthread.h>#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "errno.h"rt_event_t event1;rt_thread_t th1,th2,th3;//分別用一個位來表示事件集中的一個事件
#define EVENT_FLAG_1 (0x1 << 0)//事件1
#define EVENT_FLAG_2 (0x1 << 1)//事件2
#define EVENT_FLAG_3 (0x1 << 2)//事件3void th1_entry(void *parameter)//線程1入口函數
{while(1)//循環執行{rt_event_recv(event1, EVENT_FLAG_1, RT_EVENT_FLAG_CLEAR | RT_EVENT_FLAG_AND, RT_WAITING_FOREVER, NULL);//接收事件1rt_kprintf("th1_entry...\n");rt_event_send(event1, EVENT_FLAG_2);//發送事件2rt_thread_mdelay(1000);}
}void th2_entry(void *parameter)//線程2入口函數
{while(1){rt_event_recv(event1, EVENT_FLAG_2, RT_EVENT_FLAG_CLEAR | RT_EVENT_FLAG_AND, RT_WAITING_FOREVER, NULL);//接收事件2rt_kprintf("th2_entry...\n");rt_event_send(event1, EVENT_FLAG_3);//發送事件3rt_thread_mdelay(1000);}
}void th3_entry(void *parameter)//線程3入口函數
{while(1){rt_event_recv(event1, EVENT_FLAG_3, RT_EVENT_FLAG_CLEAR | RT_EVENT_FLAG_AND, RT_WAITING_FOREVER, NULL);//接收事件3rt_kprintf("th3_entry...\n");rt_event_send(event1, EVENT_FLAG_1);//發送事件1rt_thread_mdelay(1000);}
}int main(void)
{//動態創建事件集event1 = rt_event_create("event1", RT_IPC_FLAG_FIFO);if(event1 == RT_NULL){LOG_E("event1 rt_event_create failed...\n");return -ENOMEM;}LOG_D("event1 rt_event_create successed...\n");th1 = rt_thread_create("th1", th1_entry, NULL, 512, 20, 5);if(th1 == RT_NULL){LOG_E("th1 rt_thread_create failed...\n");return -ENOMEM;}LOG_D("th1 rt_thread_create successed...\n");th2 = rt_thread_create("th2", th2_entry, NULL, 512, 20, 5);if(th2 == RT_NULL){LOG_E("th2 rt_thread_create failed...\n");return -ENOMEM;}LOG_D("th2 rt_thread_create successed...\n");th3 = rt_thread_create("th3", th3_entry, NULL, 512, 20, 5);if(th3 == RT_NULL){LOG_E("th3 rt_thread_create failed...\n");return -ENOMEM;}LOG_D("th3 rt_thread_create successed...\n");rt_thread_startup(th1);rt_thread_startup(th2);rt_thread_startup(th3);rt_event_send(event1, EVENT_FLAG_1);//先發送一次事件1return RT_EOK;
}
可以看到確實是按照線程 1、2、3 順序執行的
創建一個臨界區,觀察是否能起到保護臨界區的作用
可以看到無論每個線程的延時函數放在哪里,每個線程都是按順序執行的,對臨界區起到了保護,避免了競態現象的發生