1. 程式人生 > 實用技巧 >臨界區保護

臨界區保護

臨界資源

臨界資源是指一次僅允許一個執行緒訪問的共享資源。它可以是一個具體的硬體裝置,也可以是一個變數、一個緩衝區。

不論是硬體臨界資源,還是軟體臨界資源,多個執行緒必須互斥地對它們進行訪問。

臨界區

每個執行緒中訪問(操作)臨界資源的那段程式碼稱為臨界區(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。