临界区保护

临界资源

临界资源是指一次仅允许一个线程访问的共享资源。它可以是一个具体的硬件设备,也可以是一个变量、一个缓冲区。

不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地对它们进行访问。

临界区

每个线程中访问(操作)临界资源的那段代码称为临界区(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。

 

posted @ 2020-07-19 16:55  doitjust  阅读(622)  评论(0编辑  收藏  举报