臨界區保護
阿新 • • 發佈:2020-07-19
臨界資源
臨界資源是指一次僅允許一個執行緒訪問的共享資源。它可以是一個具體的硬體裝置,也可以是一個變數、一個緩衝區。
不論是硬體臨界資源,還是軟體臨界資源,多個執行緒必須互斥地對它們進行訪問。
臨界區
每個執行緒中訪問(操作)臨界資源的那段程式碼稱為臨界區(Critical Section),我們每次僅允許一個執行緒進入臨界區。
uint32_t value = 0; void thread1_entry(void *para) { uint32_t i = 0; for(i=0;i<10000;i++) { rt_printk("%d\r\n",value); value++; } } void thread2_entry(void *para) { rt_thread_delay(50); value = 500; }
兩個執行緒操作同一個共享資源value。
臨界區保護
RT-Thread提供了多種途徑來進行臨界區保護
- 關閉系統排程保護臨界區
- 禁止排程、關閉中斷
- 互斥測特性保護臨界區
- 訊號量、互斥量
關閉系統排程
禁止排程
禁止排程,即把排程器鎖住,不讓其進行執行緒切換。這樣就能保證當前執行的任務不被換出,直到排程器解鎖,所以禁止排程是常用的臨界區保護方法
void thread_entry(void *parameter) {while(1) { /* 排程器上鎖,上鎖後將不再切換到其他執行緒,僅響應中斷 */ rt_enter_critical(); /* 以下進入臨界區 */ ... /* 排程器解鎖 */ rt_exit_critical(); } }
關閉中斷
因為所有執行緒的排程都是建立在中斷的基礎上的,所以,當我們關閉中斷後,系統將不能再進行排程,執行緒自身也自然不會被執行緒搶佔了。
void thread_entry(void *parameter) { rt_base_t level;while(1) { /* 關閉中斷*/ level = rt_hw_interrupt_disable(); /* 以下進入臨界區 */ ... /* 使能中斷 */ rt_hw_interrupt_enable(level); } }
臨界區保護示例
本例採用關閉中斷的方式進行臨界區保護
/* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2018-08-24 yangjie the first version */ /* 程式清單:關閉中斷進行全域性變數的訪問 */ #include <rthw.h> #include <rtthread.h> #define THREAD_PRIORITY 20 #define THREAD_STACK_SIZE 512 #define THREAD_TIMESLICE 5 /* 同時訪問的全域性變數 */ static rt_uint32_t cnt; void thread_entry(void *parameter) { rt_uint32_t no; rt_uint32_t level; no = (rt_uint32_t) parameter; while (1) { /* 關閉中斷 */ level = rt_hw_interrupt_disable(); cnt += no; /* 恢復中斷 */ rt_hw_interrupt_enable(level); rt_kprintf("protect thread[%d]'s counter is %d\n", no, cnt); rt_thread_mdelay(no * 10); } } /* 使用者應用程式入口 */ int interrupt_sample(void) { rt_thread_t thread; /* 建立t1執行緒 */ thread = rt_thread_create("thread1", thread_entry, (void *)10, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (thread != RT_NULL) rt_thread_startup(thread); /* 建立t2執行緒 */ thread = rt_thread_create("thread2", thread_entry, (void *)20, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (thread != RT_NULL) rt_thread_startup(thread); return 0; } /* 匯出到 msh 命令列表中 */ MSH_CMD_EXPORT(interrupt_sample, interrupt sample);
程式建立了2個執行緒,這兩個執行緒使用的是同一個執行緒入口函式,每個執行緒都像執行緒入口函式傳遞了各自的引數,第一個執行緒傳遞的引數是10;第2個執行緒傳遞的引數是20。
第1個執行緒實現的功能是將全域性變數自增10 ;第2個執行緒實現的功能是將全域性變數自增20。