RT-Thread隱藏的寶藏之completion
阿新 • • 發佈:2021-01-20
1. completion 是什麼
completion
直接翻譯過來是完成,所以我更願意稱 rt_completion
為 完成量。在 RT-Thread 的文件中心 中講執行緒間通訊(IPC)時,只介紹了,訊號量, 互斥量, 事件集,其實 rt_completion
可以認為是輕量級的二值訊號量。
2. completion 怎麼使用
completion 的使用非常簡單
-
定義一個完成量
struct rt_completion completion;
-
初始化完成量
rt_completion_init(&completion);
-
等待完成量
rt_completion_wait(&completion);
-
釋放完成量
rt_completion_done(&completion);
3. completion 的實現
completion 的 API 非常少,可以通過簡單的程式碼去分析
-
初始化完成量
void rt_completion_init(struct rt_completion *completion) { rt_base_t level; RT_ASSERT(completion != RT_NULL); level = rt_hw_interrupt_disable(); completion->flag = RT_UNCOMPLETED; rt_list_init(&completion->suspended_list); rt_hw_interrupt_enable(level); }
幹了兩件事:
- 設定 flag 為
RT_UNCOMPLETED
- 初始化完成量的連結串列
- 設定 flag 為
-
等待完成量(以下程式碼有刪減)
rt_err_t rt_completion_wait(struct rt_completion *completion, rt_int32_t timeout) { result = RT_EOK; thread = rt_thread_self(); level = rt_hw_interrupt_disable(); if (completion->flag != RT_COMPLETED) { if (timeout == 0) { } else { /* reset thread error number */ thread->error = RT_EOK; /* suspend thread */ rt_thread_suspend(thread); /* add to suspended list */ rt_list_insert_before(&(completion->suspended_list), &(thread->tlist)); /* current context checking */ RT_DEBUG_NOT_IN_INTERRUPT; /* start timer */ if (timeout > 0) { /* reset the timeout of thread timer and start it */ rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout); rt_timer_start(&(thread->thread_timer)); } /* enable interrupt */ rt_hw_interrupt_enable(level); /* do schedule */ rt_schedule(); /* thread is waked up */ result = thread->error; level = rt_hw_interrupt_disable(); } } /* clean completed flag */ completion->flag = RT_UNCOMPLETED; return result; }
主要做了以下工作:
- 關中斷:rt_hw_interrupt_disable();
- 掛起當前執行緒:rt_thread_suspend(thread);
- 把掛起狀態插入到執行緒的連結串列中:rt_list_insert_before
- 確保當前函式執行不是在中斷中:RT_DEBUG_NOT_IN_INTERRUPT;
- 設定並啟動定時器:rt_timer_start(&(thread->thread_timer));
- 開中斷:rt_hw_interrupt_enable(level);
- 開排程器:rt_schedule();
- 獲取當前執行緒狀態:result = thread->error;
- 設定完成量的標誌位:completion->flag = RT_UNCOMPLETED;
- 返回執行緒狀態
這樣就完成了執行緒的掛起。
-
完成完成量(以下程式碼有刪減)
void rt_completion_done(struct rt_completion *completion) { level = rt_hw_interrupt_disable(); completion->flag = RT_COMPLETED; if (!rt_list_isempty(&(completion->suspended_list))) { /* there is one thread in suspended list */ struct rt_thread *thread; /* get thread entry */ thread = rt_list_entry(completion->suspended_list.next, struct rt_thread, tlist); /* resume it */ rt_thread_resume(thread); rt_hw_interrupt_enable(level); /* perform a schedule */ rt_schedule(); } }
主要做了以下工作:
- 關中斷:rt_hw_interrupt_disable();
- 設定 flag 為
RT_COMPLETED
- 檢查連結串列不為空:rt_list_isempty
- 獲取到當前等待完成量的控制代碼:rt_list_entry
- 啟動被掛起的執行緒:rt_thread_resume(thread);
- 開中斷:rt_hw_interrupt_enable(level);
- 開排程:rt_schedule();
4. completion 與訊號量的對比
- completion API 個數少,資源佔用少,只能釋放獲取,不支援多次釋放
- semaphore API 個數多,資源佔用較多,使用靈活,可以嘗試獲取,可以多次釋放,
5. completion 如何加入工程
- 標準版 RT-Thread 中的 completion 原始碼在
"\rt-thread\components\drivers\src\completion.c"
在你要使用的檔案中#include completion.h
直接就可以使用。 - Nano 版 RT-Thread 直接拷貝
completion.c
和completion.h
新增到工程就可以使用。