1. 程式人生 > 實用技巧 >RT-Thread隱藏的寶藏之completion

RT-Thread隱藏的寶藏之completion

1. completion 是什麼

completion 直接翻譯過來是完成,所以我更願意稱 rt_completion完成量。在 RT-Thread 的文件中心 中講執行緒間通訊(IPC)時,只介紹了,訊號量互斥量事件集,其實 rt_completion 可以認為是輕量級的二值訊號量

2. completion 怎麼使用

completion 的使用非常簡單

  1. 定義一個完成量

    struct rt_completion completion;
  2. 初始化完成量

    rt_completion_init(&completion);
  3. 等待完成量

    rt_completion_wait(&completion);
  4. 釋放完成量

    rt_completion_done(&completion);

3. completion 的實現

completion 的 API 非常少,可以通過簡單的程式碼去分析

  1. 初始化完成量

    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);
    }

    幹了兩件事:

    • 設定 flagRT_UNCOMPLETED
    • 初始化完成量的連結串列
  2. 等待完成量(以下程式碼有刪減)

    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;
    • 返回執行緒狀態

這樣就完成了執行緒的掛起。

  1. 完成完成量(以下程式碼有刪減)

    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();
    • 設定 flagRT_COMPLETED
    • 檢查連結串列不為空:rt_list_isempty
    • 獲取到當前等待完成量的控制代碼:rt_list_entry
    • 啟動被掛起的執行緒:rt_thread_resume(thread);
    • 開中斷:rt_hw_interrupt_enable(level);
    • 開排程:rt_schedule();

4. completion 與訊號量的對比

  1. completion API 個數少,資源佔用少,只能釋放獲取,不支援多次釋放
  2. semaphore API 個數多,資源佔用較多,使用靈活,可以嘗試獲取,可以多次釋放,

5. completion 如何加入工程

  1. 標準版 RT-Thread 中的 completion 原始碼在 "\rt-thread\components\drivers\src\completion.c"在你要使用的檔案中#include completion.h直接就可以使用。
  2. Nano 版 RT-Thread 直接拷貝completion.ccompletion.h 新增到工程就可以使用。

原文連結:https://club.rt-thread.org/ask/article/2500.html