RT-Thread--線程管理


線程管理的功能特點

  • RT-Thread系統中線程是調度的最小單位;
  • 線程分為:系統線程和用戶線程,系統線程是由 RT-Thread 內核創建的線程,用戶線程是由應用程序創建的線程,這兩類線程都會從內核對象容器中分配線程對象,當線程被刪除時,也會被從對象容器中刪除

  • RT-Thread 的線程調度器是搶占式的,主要的工作就是從就緒線程列表中查找最高優先級線程,保證最高優先級的線程能夠被運行,最高優先級的任務一旦就緒,總能得到 CPU 的使用權。
  • 當調度器調度線程切換時,先將當前線程上下文保存起來,當再切回到這個線程時,線程調度器將該線程的上下文信息恢復。

線程的工作機制

線程控制塊

  • 線程控制塊由結構體 struct rt_thread 表示,線程控制塊是操作系統用於管理線程的一個數據結構,它會存放線程的一些信息,例如優先級、線程名稱、線程狀態等,也包含線程與線程之間連接用的鏈表結構,線程等待事件集合等
  • /**
     * Thread structure
     */
    struct rt_thread
    {
        /* rt object */
        char        name[RT_NAME_MAX];                      /**< the name of thread */
        rt_uint8_t  type;                                   /**< type of object */
        rt_uint8_t  flags;                                  /**< thread's flags */
    
    #ifdef RT_USING_MODULE
        void       *module_id;                              /**< id of application module */
    #endif
    
        rt_list_t   list;                                   /**< the object list */
        rt_list_t   tlist;                                  /**< the thread list */
    
        /* stack point and entry */
        void       *sp;                                     /**< stack point */
        void       *entry;                                  /**< entry */
        void       *parameter;                              /**< parameter */
        void       *stack_addr;                             /**< stack address */
        rt_uint32_t stack_size;                             /**< stack size */
    
        /* error code */
        rt_err_t    error;                                  /**< error code */
    
        rt_uint8_t  stat;                                   /**< thread status */
    
        /* priority */
        rt_uint8_t  current_priority;                       /**< current priority */
        rt_uint8_t  init_priority;                          /**< initialized priority */
    #if RT_THREAD_PRIORITY_MAX > 32
        rt_uint8_t  number;
        rt_uint8_t  high_mask;
    #endif
        rt_uint32_t number_mask;
    
    #if defined(RT_USING_EVENT)
        /* thread event */
        rt_uint32_t event_set;
        rt_uint8_t  event_info;
    #endif
    
    #if defined(RT_USING_SIGNALS)
        rt_sigset_t     sig_pending;                        /**< the pending signals */
        rt_sigset_t     sig_mask;                           /**< the mask bits of signal */
    
        void            *sig_ret;                           /**< the return stack pointer from signal */
        rt_sighandler_t *sig_vectors;                       /**< vectors of signal handler */
        void            *si_list;                           /**< the signal infor list */
    #endif
    
        rt_ubase_t  init_tick;                              /**< thread's initialized tick */
        rt_ubase_t  remaining_tick;                         /**< remaining tick */
    
        struct rt_timer thread_timer;                       /**< built-in thread timer */
    
        void (*cleanup)(struct rt_thread *tid);             /**< cleanup function when thread exit */
    
        /* light weight process if present */
    #ifdef RT_USING_LWP
        void        *lwp;
    #endif
    
        rt_uint32_t user_data;                             /**< private user data beyond this thread */
    };
    typedef struct rt_thread *rt_thread_t;
  • nit_priority 是線程創建時指定的線程優先級,在線程運行過程當中是不會被改變的(除非用戶執行線程控制函數進行手動調整線程優先級);
  • cleanup 會在線程退出時,被空閑線程回調一次以執行用戶設置的清理現場等工作;
  • user_data 可由用戶掛接一些數據信息到線程控制塊中,以提供類似線程私有數據的實現。線程重要屬性;

線程重要屬性

線程棧

  • RT-Thread 線程具有獨立的棧,當進行線程切換時,會將當前線程的上下文存在棧中,當線程要恢復運行時,再從棧中讀取上下文信息,進行恢復。
  • 線程棧還用來存放函數中的局部變量:函數中的局部變量從線程棧空間中申請;函數中局部變量初始時從寄存器中分配(ARM 架構),當這個函數再調用另一個函數時,這些局部變量將放入棧中。
  • 於線程第一次運行,可以以手工的方式構造這個上下文來設置一些初始的環境:入口函數(PC 寄存器)、入口參數(R0 寄存器)、返回位置(LR 寄存器)、當前機器運行狀態(CPSR 寄存器)。
  • 線程棧的增長方向是芯片構架密切相關的,RT-Thread 3.1.0 以前的版本,均只支持棧由高地址向低地址增長的方式,對於 ARM Cortex-M 架構,線程棧可構造如下圖所示

  • 線程棧大小可以這樣設定:對於資源相對較大的 MCU,可以適當設計較大的線程棧;也可以在初始時設置較大的棧,例如指定大小為 1K 或 2K 字節,然后在 FinSH 中用 list_thread 命令查看線程運行的過程中線程所使用的棧的大小,通過此命令,能夠看到從線程啟動運行時,到當前時刻點,線程使用的最大棧深度,而后加上適當的余量形成最終的線程棧大小,最后對棧空間大小加以修改。

線程狀態

  • 線程運行的過程中,同一時間內只允許一個線程在處理器中運行,從運行的過程上划分,線程有多種不同的運行狀態,如初始狀態、掛起狀態、就緒狀態等。在 RT-Thread 中,線程包含五種狀態,操作系統會自動根據它運行的情況來動態調整它的狀態;
  • 初始狀態:當線程剛開始創建還沒開始運行時就處於初始狀態;在初始狀態下,線程不參與調度。此狀態在 RT-Thread 中的宏定義為 RT_THREAD_INIT;
  • 就緒狀態:在就緒狀態下,線程按照優先級排隊,等待被執行;一旦當前線程運行完畢讓出處理器,操作系統會馬上尋找最高優先級的就緒態線程運行。此狀態在 RT-Thread 中的宏定義為 RT_THREAD_READY;
  • 運行狀態:線程當前正在運行。在單核系統中,只有 rt_thread_self() 函數返回的線程處於運行狀態;在多核系統中,可能就不止這一個線程處於運行狀態。此狀態在 RT-Thread 中的宏定義為 RT_THREAD_RUNNING;
  • 掛起狀態:也稱阻塞態。它可能因為資源不可用而掛起等待,或線程主動延時一段時間而掛起。在掛起狀態下,線程不參與調度。此狀態在 RT-Thread 中的宏定義為 RT_THREAD_SUSPEND;
  • 關閉狀態:當線程運行結束時將處於關閉狀態。關閉狀態的線程不參與線程的調度。此狀態在 RT-Thread 中的宏定義為 RT_THREAD_CLOSE;
  • /*
     * thread state definitions
     */
    #define RT_THREAD_INIT                  0x00                //*< Initialized status */
    #define RT_THREAD_READY                 0x01                //*< Ready status */
    #define RT_THREAD_SUSPEND               0x02                //*< Suspend status */
    #define RT_THREAD_RUNNING               0x03                //*< Running status */
    #define RT_THREAD_BLOCK                 RT_THREAD_SUSPEND   //*< Blocked status */
    #define RT_THREAD_CLOSE                 0x04                //*< Closed status */

線程優先級

  • RT-Thread 最大支持 256 個線程優先級 (0\~255),數值越小的優先級越高,0 為最高優先級。在一些資源比較緊張的系統中,可以根據實際情況選擇只支持 8 個或 32 個優先級的系統配置;
  • 對於 ARM Cortex-M 系列,普遍采用 32 個優先級。最低優先級默認分配給空閑線程使用,用戶一般不使用。
  • 在系統中,當有比當前線程優先級更高的線程就緒時,當前線程將立刻被換出,高優先級線程搶占處理器運行。

時間片

  • 個線程都有時間片這個參數,但時間片僅對優先級相同的就緒態線程有效;
  • 系統對優先級相同的就緒態線程采用時間片輪轉的調度方式進行調度時,時間片起到約束線程單次運行時長的作用;
  • 假設有 2 個優先級相同的就緒態線程 A 與 B,A 線程的時間片設置為 10,B 線程的時間片設置為 5,那么當系統中不存在比 A 優先級高的就緒態線程時,系統會在 A、B 線程間來回切換執行,並且每次對 A 線程執行 10 個節拍的時長,對 B 線程執行 5 個節拍的時長,如下圖示:

線程的入口函數

  • 線程控制塊中的entry是線程的入口函數。線程的入口函數由用戶設計實現,分為:無循環模式和順序執行(有限循環模式);
  • 實時操作系統中必須注意的一點就是:線程中不能陷入死循環操作,必須要有讓出 CPU 使用權的動作,如循環中調用延時函數或者主動掛起。
  • 無限循環模式:無線循環的線程的目的,就是為了讓這個線程一直被系統循環調度運行,永不刪除
  • void thread_entry(void* paramenter)
    {
        while (1)
        {
        /* 等待事件的發生 */
    
        /* 對事件進行服務、進行處理 */
        }
    }
  • 順序執行模式:如簡單的順序語句、do whlie() 或 for()循環等,此類線程不會循環或不會永久循環,可謂是 “一次性” 線程,一定會被執行完畢。在執行完畢后,線程將被系統自動刪除。
  • static void thread_entry(void* parameter)
    {
        /* 處理事務 #1 *//* 處理事務 #2 *//* 處理事務 #3 */
    }

線程的錯誤碼

  •  一個線程就是一個執行場景,錯誤碼是與執行環境密切相關的,所以每個線程配備了一個變量用於保存錯誤碼,線程的錯誤碼有以下幾種:
  • #define RT_EOK           0 // 無錯誤     
    #define RT_ERROR         1 //  普通錯誤     
    #define RT_ETIMEOUT      2 //  超時錯誤    
    #define RT_EFULL         3 //  資源已滿     
    #define RT_EEMPTY        4 //  無資源     
    #define RT_ENOMEM        5 //  無內存   
    #define RT_ENOSYS        6 //  系統不支持   
    #define RT_EBUSY         7 //  系統忙    
    #define RT_EIO           8 //  IO 錯誤      
    #define RT_EINTR         9 //  中斷系統調用  
    #define RT_EINVAL       10 //  非法參數     

線程狀態切換

  •  RT-Thread 提供一系列的操作系統調用接口,使得線程的狀態在這五個狀態之間來回切換。幾種狀態間的轉換關系如下圖所示:

  • 線程通過調用函數 rt_thread_create/init() 進入到初始狀態(RT_THREAD_INIT);
  • 初始狀態的線程通過調用函數 rt_thread_startup() 進入到就緒狀態(RT_THREAD_READY);
  • 就緒狀態的線程被調度器調度后進入運行狀態(RT_THREAD_RUNNING);
  • 當處於運行狀態的線程調用 rt_thread_delay(),rt_sem_take(),rt_mutex_take(),rt_mb_recv() 等函數或者獲取不到資源時,將進入到掛起狀態(RT_THREAD_SUSPEND);
  • 處於掛起狀態的線程,如果等待超時依然未能獲得資源或由於其他線程釋放了資源,那么它將返回到就緒狀態。
  • 掛起狀態的線程,如果調用 rt_thread_delete/detach() 函數,將更改為關閉狀態(RT_THREAD_CLOSE);
  • 運行狀態的線程,如果運行結束,就會在線程的最后部分執行 rt_thread_exit() 函數,將狀態更改為關閉狀態。
  • 注意:RT-Thread 中,實際上線程並不存在運行狀態,就緒狀態和運行狀態是等同的。

系統線程

  • 系統線程是指由系統創建的線程,用戶線程是由用戶程序調用線程管理接口創建的線程,在 RT-Thread 內核中的系統線程有空閑線程和主線程。

空閑線程

  • 空閑線程是系統創建的最低優先級的線程,線程狀態永遠為就緒態。當系統中無其他就緒線程存在時,調度器將調度到空閑線程,它通常是一個死循環,且永遠不能被掛起。
  • 空閑線程在 RT-Thread 也有着它的特殊用途:
  • 若某線程運行完畢,系統將自動刪除線程:自動執行 rt_thread_exit() 函數,先將該線程從系統就緒隊列中刪除,再將該線程的狀態更改為關閉狀態,不再參與系統調度,然后掛入 rt_thread_defunct 僵屍隊列(資源未回收、處於關閉狀態的線程隊列)中,最后空閑線程會回收被刪除線程的資源。
  • 空閑線程也提供了接口來運行用戶設置的鈎子函數,在空閑線程運行時會調用該鈎子函數,適合鈎入功耗管理、看門狗喂狗等工作。
  • /**
     * @ingroup Hook
     * This function sets a hook function to idle thread loop. When the system performs
     * idle loop, this hook function should be invoked.
     *
     * @param hook the specified hook function
     *
     * @return RT_EOK: set OK
     *         -RT_EFULL: hook list is full
     *
     * @note the hook function must be simple and never be blocked or suspend.
     */
    rt_err_t rt_thread_idle_sethook(void (*hook)(void))
    
    /**
     * delete the idle hook on hook list
     *
     * @param hook the specified hook function
     *
     * @return RT_EOK: delete OK
     *         -RT_ENOSYS: hook was not found
     */
    rt_err_t rt_thread_idle_delhook(void (*hook)(void))

主線程

  • 在系統啟動時,系統會創建 main 線程,它的入口函數為 main_thread_entry(),用戶的應用入口函數 main() 就是從這里真正開始的,系統調度器啟動后,main 線程就開始運行,過程如下圖,就可以在 main() 函數里添加自己的應用程序初始化代碼。

線程的管理方式

  • 線程的相關操作,包含:創建 / 初始化線程、啟動線程、運行線程、刪除 / 脫離線程。
  • 可以使用 rt_thread_create() 創建一個動態線程;
  • 使用 rt_thread_init() 初始化一個靜態線程;
  • 動態線程與靜態線程的區別是:動態線程是系統自動從動態內存堆上分配棧空間與線程句柄(初始化 heap 之后才能使用 create 創建動態線程),靜態線程是由用戶分配棧空間與線程句柄。

創建和刪除線程

  • 一個線程要成為可執行的對象,就必須由操作系統的內核來為它創建一個線程。可以通過如下的接口創建一個動態線程:

  • /**
     * This function will create a thread object and allocate thread object memory
     * and stack.
     *
     * @param name the name of thread, which shall be unique
     * @param entry the entry function of thread
     * @param parameter the parameter of thread enter function
     * @param stack_size the size of thread stack
     * @param priority the priority of thread
     * @param tick the time slice if there are same priority thread
     *
     * @return the created thread object,RT_EOK on RT_ERROR
     */
    rt_thread_t rt_thread_create(const char *name,
                                 void (*entry)(void *parameter),
                                 void       *parameter,
                                 rt_uint32_t stack_size,
                                 rt_uint8_t  priority,
                                 rt_uint32_t tick)
  • 對於一些使用 rt_thread_create() 創建出來的線程,當不需要使用,或者運行出錯時,我們可以使用 rt_thread_delete 函數來從系統中把線程完全刪除掉:

  • /**
     * This function will delete a thread. The thread object will be removed from
     * thread queue and deleted from system object management in the idle thread.
     *
     * @param thread the thread to be deleted
     *
     * @return the operation status, RT_EOK on OK, -RT_ERROR on error
     */
    rt_err_t rt_thread_delete(rt_thread_t thread)

初始化和脫離線程

  • 線程的初始化可以使用下面的函數接口完成,來初始化靜態線程對象:
  • /**
     * This function will initialize a thread, normally it's used to initialize a
     * static thread object.
     *
     * @param thread the static thread object
     * @param name the name of thread, which shall be unique
     * @param entry the entry function of thread
     * @param parameter the parameter of thread enter function
     * @param stack_start the start address of thread stack
     * @param stack_size the size of thread stack
     * @param priority the priority of thread
     * @param tick the time slice if there are same priority thread
     *
     * @return the operation status, RT_EOK on OK, -RT_ERROR on error
     */
    rt_err_t rt_thread_init(struct rt_thread *thread,
                            const char       *name,
                            void (*entry)(void *parameter),
                            void             *parameter,
                            void             *stack_start,
                            rt_uint32_t       stack_size,
                            rt_uint8_t        priority,
                            rt_uint32_t       tick)
  • 靜態線程的線程句柄(或者說線程控制塊指針)、線程棧由用戶提供。靜態線程是指線程控制塊、線程運行棧一般都設置為全局變量,在編譯時就被確定、被分配處理,內核不負責動態分配內存空間。需要注意的是,用戶提供的棧首地址需做系統對齊(例如 ARM 上需要做 4 字節對齊)。
  • 對於用 rt_thread_init() 初始化的線程,使用 rt_thread_detach() 將使線程對象在線程隊列和內核對象管理器中被脫離。線程脫離函數如下:
  • /**
     * This function will detach a thread. The thread object will be removed from
     * thread queue and detached/deleted from system object management.
     *
     * @param thread the thread to be deleted
     *
     * @return the operation status, RT_EOK on OK, -RT_ERROR on error
     */
    rt_err_t rt_thread_detach(rt_thread_t thread)
  • 這個函數接口是和 rt_thread_delete() 函數相對應的, rt_thread_delete() 函數操作的對象是 rt_thread_create() 創建的句柄,而 rt_thread_detach() 函數操作的對象是使用 rt_thread_init() 函數初始化的線程控制塊。

啟動線程

  • 創建(初始化)的線程狀態處於初始狀態,並未進入就緒線程的調度隊列,我們可以在線程初始化 / 創建成功后調用 rt_rtthread_startup() 函數接口讓該線程進入就緒態:

  • /**
     * This function will start a thread and put it to system ready queue
     *
     * @param thread the thread to be started
     *
     * @return the operation status, RT_EOK on OK, -RT_ERROR on error
     */
    rt_err_t rt_thread_startup(rt_thread_t thread)

獲得當前線程

  • 程序的運行過程中,相同的一段代碼可能會被多個線程執行,在執行的時候可以通過 rt_thread_self() 函數接口獲得當前執行的線程句柄:

  • /**
     * This function will return self thread object
     *
     * @return the self thread object
     */
    rt_thread_t rt_thread_self(void)

使線程讓出處理器資源

  • 當前線程的時間片用完或者該線程主動要求讓出處理器資源時,它將不再占有處理器,調度器會選擇相同優先級的下一個線程執行。線程讓出處理器可以使用 rt_err_t rt_thread_yield() 函數

  • /**
     * This function will let current thread yield processor, and scheduler will
     * choose a highest thread to run. After yield processor, the current thread
     * is still in READY state.
     *
     * @return RT_EOK
     */
    rt_err_t rt_thread_yield(void)
  • 調用該函數后,當前線程首先把自己從它所在的就緒優先級線程隊列中刪除,然后把自己掛到這個優先級隊列鏈表的尾部,然后激活調度器進行線程上下文切換(如果當前優先級只有這一個線程,則這個線程繼續執行,不進行上下文切換動作)。
  • rt_thread_yield() 函數和 rt_schedule() 函數比較相像,但在有相同優先級的其他就緒態線程存在時,系統的行為卻完全不一樣。執行 rt_thread_yield() 函數后,當前線程被換出,相同優先級的下一個就緒線程將被執行。而執行 rt_schedule() 函數后,當前線程並不一定被換出,即使被換出,也不會被放到就緒線程鏈表的尾部,而是在系統中選取就緒的優先級最高的線程執行(如果系統中沒有比當前線程優先級更高的線程存在,那么執行完 rt_schedule() 函數后,系統將繼續執行當前線程)。

使線程睡眠

  • 在實際應用中,我們有時需要讓運行的當前線程延遲一段時間,在指定的時間到達后重新運行,這就叫做 “線程睡眠”。線程睡眠可使用以下三個函數接口,這三個函數接口的作用相同,調用它們可以使當前線程掛起一段指定的時間,當這個時間過后,線程會被喚醒並再次進入就緒狀態。

  • /**
     * This function will let current thread sleep for some ticks.
     *
     * @param tick the sleep ticks
     *
     * @return RT_EOK
     */
    rt_err_t rt_thread_sleep(rt_tick_t tick)
    
    /**
     * This function will let current thread delay for some ticks.
     *
     * @param tick the delay ticks
     *
     * @return RT_EOK
     */
    rt_err_t rt_thread_delay(rt_tick_t tick)
    
    /**
     * This function will let current thread delay for some milliseconds.
     *
     * @param tick the delay time
     *
     * @return RT_EOK
     */
    rt_err_t rt_thread_mdelay(rt_int32_t ms)

掛起和恢復線程

  • 當線程調用 rt_thread_delay() 時,線程將主動掛起;當調用 rt_sem_take(),rt_mb_recv() 等函數時,資源不可使用也將導致線程掛起。處於掛起狀態的線程,如果其等待的資源超時(超過其設定的等待時間),那么該線程將不再等待這些資源,並返回到就緒狀態;或者,當其他線程釋放掉該線程所等待的資源時,該線程也會返回到就緒狀態。
  • /**
     * This function will suspend the specified thread.
     *
     * @param thread the thread to be suspended
     *
     * @return the operation status, RT_EOK on OK, -RT_ERROR on error
     *
     * @note if suspend self thread, after this function call, the
     * rt_schedule() must be invoked.
     */
    rt_err_t rt_thread_suspend(rt_thread_t thread)
  • 注意:通常不應該使用這個函數來掛起線程本身,如果確實需要采用 rt_thread_suspend() 函數掛起當前任務,需要在調用 rt_thread_suspend() 函數后立刻調用 rt_schedule() 函數進行手動的線程上下文切換。只需要了解,不推薦使用;
  • 恢復線程就是讓掛起的線程重新進入就緒狀態,並將線程放入系統的就緒隊列中;如果被恢復線程在所有就緒態線程中,位於最高優先級鏈表的第一位,那么系統將進行線程上下文的切換。
  • /**
     * This function will resume a thread and put it to system ready queue.
     *
     * @param thread the thread to be resumed
     *
     * @return the operation status, RT_EOK on OK, -RT_ERROR on error
     */
    rt_err_t rt_thread_resume(rt_thread_t thread)

控制線程

  • 當需要對線程進行一些其他控制時,例如動態更改線程的優先級時,可以使用 rt_rtthread_control()函數

  • /**
     * This function will control thread behaviors according to control command.
     *
     * @param thread the specified thread to be controlled
     * @param cmd the control command, which includes
     *  RT_THREAD_CTRL_CHANGE_PRIORITY for changing priority level of thread;
     *  RT_THREAD_CTRL_STARTUP for starting a thread;
     *  RT_THREAD_CTRL_CLOSE for delete a thread.
     * @param arg the argument of control command
     *
     * @return RT_EOK
     */
    rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg)
  • 指示控制命令 cmd 當前支持的命令包括:

  • RT_THREAD_CTRL_CHANGE_PRIORITY:動態更改線程的優先級;

  • RT_THREAD_CTRL_STARTUP:開始運行一個線程,等同於 rt_thread_startup() 函數調用;

  • RT_THREAD_CTRL_CLOSE:關閉一個線程,等同於 rt_thread_delete() 函數調用。

設置和刪除空閑鈎子

  • 空閑鈎子函數是空閑線程的鈎子函數,如果設置了空閑鈎子函數,就可以在系統執行空閑線程時,自動執行空閑鈎子函數來做一些其他事情,比如系統指示燈。

  • /**
     * @ingroup Hook
     * This function sets a hook function to idle thread loop. When the system performs
     * idle loop, this hook function should be invoked.
     *
     * @param hook the specified hook function
     *
     * @return RT_EOK: set OK
     *         -RT_EFULL: hook list is full
     *
     * @note the hook function must be simple and never be blocked or suspend.
     */
    rt_err_t rt_thread_idle_sethook(void (*hook)(void))
    
    
    /**
     * delete the idle hook on hook list
     *
     * @param hook the specified hook function
     *
     * @return RT_EOK: delete OK
     *         -RT_ENOSYS: hook was not found
     */
    rt_err_t rt_thread_idle_delhook(void (*hook)(void))
  • 空閑線程是一個線程狀態永遠為就緒態的線程,因此設置的鈎子函數必須保證空閑線程在任何時刻都不會處於掛起狀態,例如 rt_thread_delay(),rt_sem_take() 等可能會導致線程掛起的函數都不能使用。

設置調度器鈎子

  •  在整個系統的運行時,系統都處於線程運行、中斷觸發 - 響應中斷、切換到其他線程,甚至是線程間的切換過程中,或者說系統的上下文切換是系統中最普遍的事件。有時用戶可能會想知道在一個時刻發生了什么樣的線程切換,可以通過調用下面的函數接口設置一個相應的鈎子函數。在系統線程切換時,這個鈎子函數將被調用:
  • /**
     * This function will set a hook function, which will be invoked when thread
     * switch happens.
     *
     * @param hook the hook function
     */
    void rt_scheduler_sethook(void (*hook)(struct rt_thread *from, struct rt_thread *to))
  • 鈎子函數 hook() 的聲明如下:

  • void hook(struct rt_thread* from, struct rt_thread* to);

  • 請仔細編寫你的鈎子函數,稍有不慎將很可能導致整個系統運行不正常(在這個鈎子函數中,基本上不允許調用系統 API,更不應該導致當前運行的上下文掛起)。

線程應用實例

創建線程

  • 創建一個動態線程,初始化一個靜態線程,一個線程在運行完畢后自動被系統刪除,另一個一直打印計數:
  • #include <rtthread.h>
    
    #define THREAD_PRIORITY         25
    #define THREAD_STACK_SIZE       512
    #define THREAD_TIMESLICE        5
    
    static rt_thread_t tid1 = RT_NULL;
    
    /* 線程 1 的入口函數 */
    static void thread1_entry(void *parameter)
    {
        rt_uint32_t count = 0;
    
        while (1)
        {
            /* 線程 1 采用低優先級運行,一直打印計數值 */
            rt_kprintf("thread1 count: %d\n", count ++);
            rt_thread_mdelay(500);
        }
    }
    
    ALIGN(RT_ALIGN_SIZE)
    static char thread2_stack[1024];
    static struct rt_thread thread2;
    /* 線程 2 入口 */
    static void thread2_entry(void *param)
    {
        rt_uint32_t count = 0;
    
        /* 線程 2 擁有較高的優先級,以搶占線程 1 而獲得執行 */
        for (count = 0; count < 10 ; count++)
        {
            /* 線程 2 打印計數值 */
            rt_kprintf("thread2 count: %d\n", count);
        }
        rt_kprintf("thread2 exit\n");
        /* 線程 2 運行結束后也將自動被系統脫離 */
    }
    
    /* 線程示例 */
    int thread_sample(void)
    {
        /* 創建線程 1,名稱是 thread1,入口是 thread1_entry*/
        tid1 = rt_thread_create("thread1",
                                thread1_entry, RT_NULL,
                                THREAD_STACK_SIZE,
                                THREAD_PRIORITY, THREAD_TIMESLICE);
    
        /* 如果獲得線程控制塊,啟動這個線程 */
        if (tid1 != RT_NULL)
            rt_thread_startup(tid1);
    
        /* 初始化線程 2,名稱是 thread2,入口是 thread2_entry */
        rt_thread_init(&thread2,
                       "thread2",
                       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(thread_sample, thread sample);
  • 運行結果
  • \ | /
    - RT -     Thread Operating System
     / | \     3.1.0 build Aug 24 2018
     2006 - 2018 Copyright by rt-thread team
    msh >thread_sample
    msh >thread2 count: 0
    thread2 count: 1
    thread2 count: 2
    thread2 count: 3
    thread2 count: 4
    thread2 count: 5
    thread2 count: 6
    thread2 count: 7
    thread2 count: 8
    thread2 count: 9
    thread2 exit
    thread1 count: 0
    thread1 count: 1
    thread1 count: 2
    thread1 count: 3

     

  • 關於刪除線程:大多數線程是循環執行的,無需刪除;而能運行完畢的線程,RT-Thread 在線程運行完畢后,自動刪除線程,在 rt_thread_exit() 里完成刪除動作。用戶只需要了解該接口的作用,不推薦使用該接口;

線程時間片輪轉調度示例

  • 創建兩個線程,在執行時會一直打印計數
  • #include <rtthread.h>
    
    #define THREAD_STACK_SIZE   1024
    #define THREAD_PRIORITY     20
    #define THREAD_TIMESLICE    10
    
    /* 線程入口 */
    static void thread_entry(void* parameter)
    {
        rt_uint32_t value;
        rt_uint32_t count = 0;
    
        value = (rt_uint32_t)parameter;
        while (1)
        {
            if(0 == (count % 5))
            {
                rt_kprintf("thread %d is running ,thread %d count = %d\n", value , value , count);
    
                if(count> 200)
                    return;
            }
             count++;
         }
    }
    
    int timeslice_sample(void)
    {
        rt_thread_t tid = RT_NULL;
        /* 創建線程 1 */
        tid = rt_thread_create("thread1",
                                thread_entry, (void*)1,
                                THREAD_STACK_SIZE,
                                THREAD_PRIORITY, THREAD_TIMESLICE);
        if (tid != RT_NULL)
            rt_thread_startup(tid);
    
    
        /* 創建線程 2 */
        tid = rt_thread_create("thread2",
                                thread_entry, (void*)2,
                                THREAD_STACK_SIZE,
                                THREAD_PRIORITY, THREAD_TIMESLICE-5);
        if (tid != RT_NULL)
            rt_thread_startup(tid);
        return 0;
    }
    
    /* 導出到 msh 命令列表中 */
    MSH_CMD_EXPORT(timeslice_sample, timeslice sample);
  • 運行結果
  •  \ | /
    - RT -     Thread Operating System
     / | \     3.1.0 build Aug 27 2018
     2006 - 2018 Copyright by rt-thread team
    msh >timeslice_sample
    msh >thread 1 is running ,thread 1 count = 0
    thread 1 is running ,thread 1 count = 5
    thread 1 is running ,thread 1 count = 10
    thread 1 is running ,thread 1 count = 15
    …
    thread 1 is running ,thread 1 count = 125
    thread 1 is rthread 2 is running ,thread 2 count = 0
    thread 2 is running ,thread 2 count = 5
    thread 2 is running ,thread 2 count = 10
    thread 2 is running ,thread 2 count = 15
    thread 2 is running ,thread 2 count = 20
    thread 2 is running ,thread 2 count = 25
    thread 2 is running ,thread 2 count = 30
    thread 2 is running ,thread 2 count = 35
    thread 2 is running ,thread 2 count = 40
    thread 2 is running ,thread 2 count = 45
    thread 2 is running ,thread 2 count = 50
    thread 2 is running ,thread 2 count = 55
    thread 2 is running ,thread 2 count = 60
    thread 2 is running ,thread 2 cunning ,thread 2 count = 65
    thread 1 is running ,thread 1 count = 135
    …
    thread 2 is running ,thread 2 count = 205

     

線程調度器鈎子示例

  • 在線程進行調度切換時,會執行調度,我們可以設置一個調度器鈎子,這樣可以在線程切換時,做一些額外的事情
  • #include <rtthread.h>
    
    #define THREAD_STACK_SIZE   1024
    #define THREAD_PRIORITY     20
    #define THREAD_TIMESLICE    10
    
    /* 針對每個線程的計數器 */
    volatile rt_uint32_t count[2];
    
    /* 線程 1、2 共用一個入口,但入口參數不同 */
    static void thread_entry(void* parameter)
    {
        rt_uint32_t value;
    
        value = (rt_uint32_t)parameter;
        while (1)
        {
            rt_kprintf("thread %d is running\n", value);
            rt_thread_mdelay(1000); // 延時一段時間
        }
    }
    
    static rt_thread_t tid1 = RT_NULL;
    static rt_thread_t tid2 = RT_NULL;
    
    static void hook_of_scheduler(struct rt_thread* from, struct rt_thread* to)
    {
        rt_kprintf("from: %s -->  to: %s \n", from->name , to->name);
    }
    
    int scheduler_hook(void)
    {
        /* 設置調度器鈎子 */
        rt_scheduler_sethook(hook_of_scheduler);
    
        /* 創建線程 1 */
        tid1 = rt_thread_create("thread1",
                                thread_entry, (void*)1,
                                THREAD_STACK_SIZE,
                                THREAD_PRIORITY, THREAD_TIMESLICE);
        if (tid1 != RT_NULL)
            rt_thread_startup(tid1);
    
        /* 創建線程 2 */
        tid2 = rt_thread_create("thread2",
                                thread_entry, (void*)2,
                                THREAD_STACK_SIZE,
                                THREAD_PRIORITY,THREAD_TIMESLICE - 5);
        if (tid2 != RT_NULL)
            rt_thread_startup(tid2);
        return 0;
    }
    
    /* 導出到 msh 命令列表中 */
    MSH_CMD_EXPORT(scheduler_hook, scheduler_hook sample);

     

  • 運行結果

  •  \ | /
    - RT -     Thread Operating System
     / | \     3.1.0 build Aug 27 2018
     2006 - 2018 Copyright by rt-thread team
    msh > scheduler_hook
    msh >from: tshell -->  to: thread1
    thread 1 is running
    from: thread1 -->  to: thread2
    thread 2 is running
    from: thread2 -->  to: tidle
    from: tidle -->  to: thread1
    thread 1 is running
    from: thread1 -->  to: tidle
    from: tidle -->  to: thread2
    thread 2 is running 

參考

  • 《RT-Thread 編程指南》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM