線程間同步
- 一、信號量
- 1. 創建信號量
- 2. 獲取信號量
- 3. 釋放信號量
- 4. 刪除信號量
- 5. 代碼示例
- 二、互斥量
- 1. 創建互斥量
- 2. 獲取互斥量
- 3. 釋放互斥量
- 4. 刪除互斥量
- 5. 代碼示例
- 三、事件集
- 1. 創建事件集
- 2. 發送事件
- 3. 接收事件
- 4. 刪除事件集
- 5. 代碼示例
簡單來說,同步就是多個線程同時訪問一塊內存,好比如 一個線程向指定內存中寫入一個數據,另一個線程就從該內存中讀取數據,這就是“同步”。
線程的同步方式有很多種,其核心思想都是:在訪問臨界區的時候只允許一個 (或一類) 線程運行。
(以下都以動態創建方式介紹)
一、信號量
信號量是一種輕型的用于解決線程間同步問題的內核對象,線程可以獲取或釋放它,從而達到同步或互斥的目的。
每個信號量對象都有一個信號量值和一個線程等待隊列,信號量的值對應了信號量對象的實例數目、資源數目,假如信號量值為 5,則表示共有 5 個信號量實例(資源)可以被使用,當信號量實例數目為零時,再申請該信號量的線程就會被掛起在該信號量的等待隊列上,等待可用的信號量實例(資源)。
1. 創建信號量
當創建一個信號量時,內核首先創建一個信號量控制塊,然后對該控制塊進行基本的初始化工作
rt_sem_t rt_sem_create(const char *name,rt_uint32_t value,rt_uint8_t flag);
注: RT_IPC_FLAG_FIFO(先進先出)方式時,那么等待線程隊列將按照先進先出的方式排隊,先進入的線程將先獲得等待的信號量;當選擇 RT_IPC_FLAG_PRIO(優先級等待)方式時,等待線程隊列將按照優先級進行排隊,優先級高的等待線程將先獲得等待的信號量。。
2. 獲取信號量
線程通過獲取信號量來獲得信號量資源實例,當信號量值大于零時,線程將獲得信號量,并且相應的信號量值會減 1
rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time);
在調用這個函數時,如果信號量的值等于零,那么說明當前信號量資源實例不可用,申請該信號量的線程將根據 time 參數的情況選擇直接返回、或掛起等待一段時間、或永久等待,直到其他線程或中斷釋放該信號量。如果在參數 time 指定的時間內依然得不到信號量,線程將超時返回,返回值是 - RT_ETIMEOUT。
3. 釋放信號量
釋放信號量可以喚醒掛起在該信號量上的線程
rt_err_t rt_sem_release(rt_sem_t sem);
當信號量的值等于零時,并且有線程等待這個信號量時,釋放信號量將喚醒等待在該信號量線程隊列中的第一個線程,由它獲取信號量;否則將把信號量的值加 1
4. 刪除信號量
系統不再使用信號量時,可通過刪除信號量以釋放系統資源,適用于動態創建的信號量
rt_err_t rt_sem_delete(rt_sem_t sem);
調用這個函數時,系統將刪除這個信號量。如果刪除該信號量時,有線程正在等待該信號量,那么刪除操作會先喚醒等待在該信號量上的線程(等待線程的返回值是 - RT_ERROR),然后再釋放信號量的內存資源
5. 代碼示例
這是一個信號量使用例程,該例程創建了一個動態信號量,初始化兩個線程,一個線程發送信號量,一個線程接收到信號量后,執行相應的操作
#include <rtthread.h>#define THREAD_PRIORITY 25
#define THREAD_TIMESLICE 5/* 指向信號量的指針 */
static rt_sem_t dynamic_sem = RT_NULL;ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;
static void rt_thread1_entry(void *parameter)
{static rt_uint8_t count = 0;while(1){if(count <= 100){count++;}elsereturn;/* count 每計數 10 次,就釋放一次信號量 */if(0 == (count % 10)){rt_kprintf("t1 release a dynamic semaphore.\n");rt_sem_release(dynamic_sem);}}
}ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
static void rt_thread2_entry(void *parameter)
{static rt_err_t result;static rt_uint8_t number = 0;while(1){/* 永久方式等待信號量,獲取到信號量,則執行 number 自加的操作 */result = rt_sem_take(dynamic_sem, RT_WAITING_FOREVER);if (result != RT_EOK){rt_kprintf("t2 take a dynamic semaphore, failed.\n");rt_sem_delete(dynamic_sem);return;}else{number++;rt_kprintf("t2 take a dynamic semaphore. number = %d\n" ,number);}}
}/* 信號量示例的初始化 */
int semaphore_sample(void)
{/* 創建一個動態信號量,初始值是 0 */dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_PRIO);if (dynamic_sem == RT_NULL){rt_kprintf("create dynamic semaphore failed.\n");return -1;}else{rt_kprintf("create done. dynamic semaphore value = 0.\n");}rt_thread_init(&thread1,"thread1",rt_thread1_entry,RT_NULL,&thread1_stack[0],sizeof(thread1_stack),THREAD_PRIORITY, THREAD_TIMESLICE);rt_thread_startup(&thread1);rt_thread_init(&thread2,"thread2",rt_thread2_entry,RT_NULL,&thread2_stack[0],sizeof(thread2_stack),THREAD_PRIORITY-1, THREAD_TIMESLICE);rt_thread_startup(&thread2);return 0;
}
/* 導出到 msh 命令列表中 */
MSH_CMD_EXPORT(semaphore_sample, semaphore sample);
運行結果如下:
\ | /
- RT - Thread Operating System/ | \ 3.1.0 build Aug 27 20182006 - 2018 Copyright by rt-thread team
msh >semaphore_sample
create done. dynamic semaphore value = 0.
msh >t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number = 1
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number = 2
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number = 3
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number = 4
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number = 5
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number = 6
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number = 7
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number = 8
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number = 9
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number = 10
二、互斥量
互斥量又叫相互排斥的信號量,是一種特殊的二值信號量。互斥量類似于只有一個車位的停車場:當有一輛車進入的時候,將停車場大門鎖住,其他車輛在外面等候。當里面的車出來時,將停車場大門打開,下一輛車才可以進入。
互斥量和信號量不同的是:擁有互斥量的線程擁有互斥量的所有權,互斥量支持遞歸訪問且能防止線程優先級翻轉;并且互斥量只能由持有線程釋放,而信號量則可以由任何線程釋放。
互斥量的狀態只有兩種,開鎖或閉鎖(兩種狀態值)。當有線程持有它時,互斥量處于閉鎖狀態,由這個線程獲得它的所有權。相反,當這個線程釋放它時,將對互斥量進行開鎖,失去它的所有權。當一個線程持有互斥量時,其他線程將不能夠對它進行開鎖或持有它,持有該互斥量的線程也能夠再次獲得這個鎖而不被掛起
在 RT-Thread 操作系統中,互斥量可以解決優先級翻轉問題,實現的是優先級繼承協議 (Sha, 1990)。優先級繼承是通過在線程 A 嘗試獲取共享資源而被掛起的期間內,將線程 C 的優先級提升到線程 A 的優先級別,從而解決優先級翻轉引起的問題。這樣能夠防止 C(間接地防止 A)被 B 搶占,如下圖所示。優先級繼承是指,提高某個占有某種資源的低優先級線程的優先級,使之與所有等待該資源的線程中優先級最高的那個線程的優先級相等,然后執行,而當這個低優先級線程釋放該資源時,優先級重新回到初始設定。因此,繼承優先級的線程避免了系統資源被任何中間優先級的線程搶占。
1. 創建互斥量
創建一個互斥量時,內核首先創建一個互斥量控制塊,然后完成對該控制塊的初始化工作
rt_mutex_t rt_mutex_create (const char* name, rt_uint8_t flag);
注: 互斥量的 flag 標志已經作廢,無論用戶選擇 RT_IPC_FLAG_PRIO 還是 RT_IPC_FLAG_FIFO,內核均按照 RT_IPC_FLAG_PRIO 處理
2. 獲取互斥量
線程獲取了互斥量,那么線程就有了對該互斥量的所有權,即某一個時刻一個互斥量只能被一個線程持有
rt_err_t rt_mutex_take (rt_mutex_t mutex, rt_int32_t time);
如果互斥量沒有被其他線程控制,那么申請該互斥量的線程將成功獲得該互斥量。如果互斥量已經被當前線程線程控制,則該互斥量的持有計數加 1,當前線程也不會掛起等待。如果互斥量已經被其他線程占有,則當前線程在該互斥量上掛起等待,直到其他線程釋放它或者等待時間超過指定的超時時間
3. 釋放互斥量
當線程完成互斥資源的訪問后,應盡快釋放它占據的互斥量,使得其他線程能及時獲取該互斥量
rt_err_t rt_mutex_release(rt_mutex_t mutex);
使用該函數接口時,只有已經擁有互斥量控制權的線程才能釋放它,每釋放一次該互斥量,它的持有計數就減 1。當該互斥量的持有計數為零時(即持有線程已經釋放所有的持有操作),它變為可用,等待在該信號量上的線程將被喚醒。如果線程的運行優先級被互斥量提升,那么當互斥量被釋放后,線程恢復為持有互斥量前的優先級
4. 刪除互斥量
當不再使用互斥量時,通過刪除互斥量以釋放系統資源,適用于動態創建的互斥量
rt_err_t rt_mutex_delete (rt_mutex_t mutex);
當刪除一個互斥量時,所有等待此互斥量的線程都將被喚醒,等待線程獲得的返回值是 - RT_ERROR。然后系統將該互斥量從內核對象管理器鏈表中刪除并釋放互斥量占用的內存空間
5. 代碼示例
這是一個互斥量的應用例程,互斥鎖是一種保護共享資源的方法。當一個線程擁有互斥鎖的時候,可以保護共享資源不被其他線程破壞。下面用一個例子來說明,有兩個線程:線程 1 和線程 2,線程 1 對 2 個 number 分別進行加 1 操作;線程 2 也對 2 個 number 分別進行加 1 操作,使用互斥量保證線程改變 2 個 number 值的操作不被打斷
互斥量例程
#include <rtthread.h>#define THREAD_PRIORITY 8
#define THREAD_TIMESLICE 5/* 指向互斥量的指針 */
static rt_mutex_t dynamic_mutex = RT_NULL;
static rt_uint8_t number1,number2 = 0;ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;
static void rt_thread_entry1(void *parameter)
{while(1){/* 線程 1 獲取到互斥量后,先后對 number1、number2 進行加 1 操作,然后釋放互斥量 */rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);number1++;rt_thread_mdelay(10);number2++;rt_mutex_release(dynamic_mutex);}
}ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
static void rt_thread_entry2(void *parameter)
{while(1){/* 線程 2 獲取到互斥量后,檢查 number1、number2 的值是否相同,相同則表示 mutex 起到了鎖的作用 */rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);if(number1 != number2){rt_kprintf("not protect.number1 = %d, mumber2 = %d \n",number1 ,number2);}else{rt_kprintf("mutex protect ,number1 = mumber2 is %d\n",number1);}number1++;number2++;rt_mutex_release(dynamic_mutex);if(number1>=50)return;}
}/* 互斥量示例的初始化 */
int mutex_sample(void)
{/* 創建一個動態互斥量 */dynamic_mutex = rt_mutex_create("dmutex", RT_IPC_FLAG_PRIO);if (dynamic_mutex == RT_NULL){rt_kprintf("create dynamic mutex failed.\n");return -1;}rt_thread_init(&thread1,"thread1",rt_thread_entry1,RT_NULL,&thread1_stack[0],sizeof(thread1_stack),THREAD_PRIORITY, THREAD_TIMESLICE);rt_thread_startup(&thread1);rt_thread_init(&thread2,"thread2",rt_thread_entry2,RT_NULL,&thread2_stack[0],sizeof(thread2_stack),THREAD_PRIORITY-1, THREAD_TIMESLICE);rt_thread_startup(&thread2);return 0;
}/* 導出到 MSH 命令列表中 */
MSH_CMD_EXPORT(mutex_sample, mutex sample);
運行結果如下:
\ | /
- RT - Thread Operating System/ | \ 3.1.0 build Aug 24 20182006 - 2018 Copyright by rt-thread team
msh >mutex_sample
msh >mutex protect ,number1 = mumber2 is 1
mutex protect ,number1 = mumber2 is 2
mutex protect ,number1 = mumber2 is 3
mutex protect ,number1 = mumber2 is 4
…
mutex protect ,number1 = mumber2 is 48
mutex protect ,number1 = mumber2 is 49
防止優先級翻轉特性例程
#include <rtthread.h>/* 指向線程控制塊的指針 */
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;
static rt_thread_t tid3 = RT_NULL;
static rt_mutex_t mutex = RT_NULL;#define THREAD_PRIORITY 10
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 5/* 線程 1 入口 */
static void thread1_entry(void *parameter)
{/* 先讓低優先級線程運行 */rt_thread_mdelay(100);/* 此時 thread3 持有 mutex,并且 thread2 等待持有 mutex *//* 檢查 thread2 與 thread3 的優先級情況 */if (tid2->current_priority != tid3->current_priority){/* 優先級不相同,測試失敗 */rt_kprintf("the priority of thread2 is: %d\n", tid2->current_priority);rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);rt_kprintf("test failed.\n");return;}else{rt_kprintf("the priority of thread2 is: %d\n", tid2->current_priority);rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);rt_kprintf("test OK.\n");}
}/* 線程 2 入口 */
static void thread2_entry(void *parameter)
{rt_err_t result;rt_kprintf("the priority of thread2 is: %d\n", tid2->current_priority);/* 先讓低優先級線程運行 */rt_thread_mdelay(50);/** 試圖持有互斥鎖,此時 thread3 持有,應把 thread3 的優先級提升* 到 thread2 相同的優先級*/result = rt_mutex_take(mutex, RT_WAITING_FOREVER);if (result == RT_EOK){/* 釋放互斥鎖 */rt_mutex_release(mutex);}
}/* 線程 3 入口 */
static void thread3_entry(void *parameter)
{rt_tick_t tick;rt_err_t result;rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);result = rt_mutex_take(mutex, RT_WAITING_FOREVER);if (result != RT_EOK){rt_kprintf("thread3 take a mutex, failed.\n");}/* 做一個長時間的循環,500ms */tick = rt_tick_get();while (rt_tick_get() - tick < (RT_TICK_PER_SECOND / 2)) ;rt_mutex_release(mutex);
}int pri_inversion(void)
{/* 創建互斥鎖 */mutex = rt_mutex_create("mutex", RT_IPC_FLAG_PRIO);if (mutex == RT_NULL){rt_kprintf("create dynamic mutex failed.\n");return -1;}/* 創建線程 1 */tid1 = rt_thread_create("thread1",thread1_entry,RT_NULL,THREAD_STACK_SIZE,THREAD_PRIORITY - 1, THREAD_TIMESLICE);if (tid1 != RT_NULL)rt_thread_startup(tid1);/* 創建線程 2 */tid2 = rt_thread_create("thread2",thread2_entry,RT_NULL,THREAD_STACK_SIZE,THREAD_PRIORITY, THREAD_TIMESLICE);if (tid2 != RT_NULL)rt_thread_startup(tid2);/* 創建線程 3 */tid3 = rt_thread_create("thread3",thread3_entry,RT_NULL,THREAD_STACK_SIZE,THREAD_PRIORITY + 1, THREAD_TIMESLICE);if (tid3 != RT_NULL)rt_thread_startup(tid3);return 0;
}/* 導出到 msh 命令列表中 */
MSH_CMD_EXPORT(pri_inversion, prio_inversion sample);
運行結果如下:
\ | /
- RT - Thread Operating System/ | \ 3.1.0 build Aug 27 20182006 - 2018 Copyright by rt-thread team
msh >pri_inversion
the priority of thread2 is: 10
the priority of thread3 is: 11
the priority of thread2 is: 10
the priority of thread3 is: 10
test OK.
三、事件集
事件集主要用于線程間的同步,與信號量不同,它的特點是可以實現一對多,多對多的同步。即一個線程與多個事件的關系可設置為:其中任意一個事件喚醒線程,或幾個事件都到達后才喚醒線程進行后續的處理;同樣,事件也可以是多個線程同步多個事件。這種多個事件的集合可以用一個 32 位無符號整型變量來表示,變量的每一位代表一個事件,線程通過 “邏輯與” 或“邏輯或”將一個或多個事件關聯起來,形成事件組合。事件的 “邏輯或” 也稱為是獨立型同步,指的是線程與任何事件之一發生同步;事件 “邏輯與” 也稱為是關聯型同步,指的是線程與若干事件都發生同步。
RT-Thread 定義的事件集有以下特點:
- 事件只與線程相關,事件間相互獨立:每個線程可擁有 32 個事件標志,采用一個 32 bit 無符號整型數進行記錄,每一個 bit 代表一個事件;
- 事件僅用于同步,不提供數據傳輸功能;
- 事件無排隊性,即多次向線程發送同一事件 (如果線程還未來得及讀走),其效果等同于只發送一次。
在 RT-Thread 中,每個線程都擁有一個事件信息標記,它有三個屬性,分別是 RT_EVENT_FLAG_AND(邏輯與),RT_EVENT_FLAG_OR(邏輯或)以及 RT_EVENT_FLAG_CLEAR(清除標記)。當線程等待事件同步時,可以通過 32 個事件標志和這個事件信息標記來判斷當前接收的事件是否滿足同步條件。
線程 #1 的事件標志中第 1 位和第 30 位被置位,如果事件信息標記位設為邏輯與,則表示線程 #1 只有在事件 1 和事件 30 都發生以后才會被觸發喚醒,如果事件信息標記位設為邏輯或,則事件 1 或事件 30 中的任意一個發生都會觸發喚醒線程 #1。如果信息標記同時設置了清除標記位,則當線程 #1 喚醒后將主動把事件 1 和事件 30 清為零,否則事件標志將依然存在(即置 1)
1. 創建事件集
當創建一個事件集時,內核首先創建一個事件集控制塊,然后對該事件集控制塊進行基本的初始化
rt_event_t rt_event_create(const char* name, rt_uint8_t flag);
調用該函數接口時,系統會從對象管理器中分配事件集對象,并初始化這個對象,然后初始化父類 IPC 對象
2. 發送事件
發送事件函數可以發送事件集中的一個或多個事件
rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set);
使用該函數接口時,通過參數 set 指定的事件標志來設定 event 事件集對象的事件標志值,然后遍歷等待在 event 事件集對象上的等待線程鏈表,判斷是否有線程的事件激活要求與當前 event 對象事件標志值匹配,如果有,則喚醒該線程
3. 接收事件
內核使用 32 位的無符號整數來標識事件集,它的每一位代表一個事件,因此一個事件集對象可同時等待接收 32 個事件,內核可以通過指定選擇參數 “邏輯與” 或“邏輯或”來選擇如何激活線程,使用 “邏輯與” 參數表示只有當所有等待的事件都發生時才激活線程,而使用 “邏輯或” 參數則表示只要有一個等待的事件發生就激活線程
rt_err_t rt_event_recv(rt_event_t event,rt_uint32_t set,rt_uint8_t option,rt_int32_t timeout,rt_uint32_t* recved);
當用戶調用這個接口時,系統首先根據 set 參數和接收選項 option 來判斷它要接收的事件是否發生,如果已經發生,則根據參數 option 上是否設置有 RT_EVENT_FLAG_CLEAR 來決定是否重置事件的相應標志位,然后返回(其中 recved 參數返回接收到的事件);如果沒有發生,則把等待的 set 和 option 參數填入線程本身的結構中,然后把線程掛起在此事件上,直到其等待的事件滿足條件或等待時間超過指定的超時時間。如果超時時間設置為零,則表示當線程要接受的事件沒有滿足其要求時就不等待,而直接返回 - RT_ETIMEOUT
option 的值可取:
/* 選擇 邏輯與 或 邏輯或 的方式接收事件 */
RT_EVENT_FLAG_OR
RT_EVENT_FLAG_AND/* 選擇清除重置事件標志位 */
RT_EVENT_FLAG_CLEAR
4. 刪除事件集
系統不再使用 rt_event_create() 創建的事件集對象時,通過刪除事件集對象控制塊來釋放系統資源
rt_err_t rt_event_delete(rt_event_t event);
5. 代碼示例
這是事件集的應用例程,例子中初始化了一個事件集,兩個線程。一個線程等待自己關心的事件發生,另外一個線程發送事件
#include <rtthread.h>#define THREAD_PRIORITY 9
#define THREAD_TIMESLICE 5#define EVENT_FLAG3 (1 << 3)
#define EVENT_FLAG5 (1 << 5)/* 事件控制塊 */
static struct rt_event event;ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;/* 線程 1 入口函數 */
static void thread1_recv_event(void *param)
{rt_uint32_t e;/* 第一次接收事件,事件 3 或事件 5 任意一個可以觸發線程 1,接收完后清除事件標志 */if (rt_event_recv(&event, (EVENT_FLAG3 | EVENT_FLAG5),RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,RT_WAITING_FOREVER, &e) == RT_EOK){rt_kprintf("thread1: OR recv event 0x%x\n", e);}rt_kprintf("thread1: delay 1s to prepare the second event\n");rt_thread_mdelay(1000);/* 第二次接收事件,事件 3 和事件 5 均發生時才可以觸發線程 1,接收完后清除事件標志 */if (rt_event_recv(&event, (EVENT_FLAG3 | EVENT_FLAG5),RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,RT_WAITING_FOREVER, &e) == RT_EOK){rt_kprintf("thread1: AND recv event 0x%x\n", e);}rt_kprintf("thread1 leave.\n");
}ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;/* 線程 2 入口 */
static void thread2_send_event(void *param)
{rt_kprintf("thread2: send event3\n");rt_event_send(&event, EVENT_FLAG3);rt_thread_mdelay(200);rt_kprintf("thread2: send event5\n");rt_event_send(&event, EVENT_FLAG5);rt_thread_mdelay(200);rt_kprintf("thread2: send event3\n");rt_event_send(&event, EVENT_FLAG3);rt_kprintf("thread2 leave.\n");
}int event_sample(void)
{rt_err_t result;/* 初始化事件對象 */result = rt_event_init(&event, "event", RT_IPC_FLAG_PRIO);if (result != RT_EOK){rt_kprintf("init event failed.\n");return -1;}rt_thread_init(&thread1,"thread1",thread1_recv_event,RT_NULL,&thread1_stack[0],sizeof(thread1_stack),THREAD_PRIORITY - 1, THREAD_TIMESLICE);rt_thread_startup(&thread1);rt_thread_init(&thread2,"thread2",thread2_send_event,RT_NULL,&thread2_stack[0],sizeof(thread2_stack),THREAD_PRIORITY, THREAD_TIMESLICE);rt_thread_startup(&thread2);return 0;
}/* 導出到 msh 命令列表中 */
MSH_CMD_EXPORT(event_sample, event sample);
運行結果如下:
\ | /
- RT - Thread Operating System/ | \ 3.1.0 build Aug 24 20182006 - 2018 Copyright by rt-thread team
msh >event_sample
thread2: send event3
thread1: OR recv event 0x8
thread1: delay 1s to prepare the second event
msh >thread2: send event5
thread2: send event3
thread2 leave.
thread1: AND recv event 0x28
thread1 leave.