• 临界区保护


    临界资源

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

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

    临界区

    每个线程中访问(操作)临界资源的那段代码称为临界区(Critical Section),我们每次仅允许一个线程进入临界区。

    uint32_t value = 0;
    
    void thread1_entry(void *para)
    {
        uint32_t i = 0;
        
        for(i=0;i<10000;i++)
        {
            rt_printk("%d
    ",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
    ", 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。

  • 相关阅读:
    25个可遇不可求的jQuery插件
    微网站|h5弹窗|手机网站 html5 弹窗、弹层、提示框、加载条
    逻辑设计--每一层的验证策略
    [傅里叶变换及其应用学习笔记] 八. 时延性,尺度变化,卷积
    [傅里叶变换及其应用学习笔记] 七. 傅里叶正(反)变换复习
    [傅里叶变换及其应用学习笔记] 六. 热方程讨论
    [傅里叶变换及其应用学习笔记] 五. 傅里叶级数连续性讨论,热方程
    [傅里叶变换及其应用学习笔记] 四. 傅里叶级数
    [傅里叶变换及其应用学习笔记] 三. 复习,将一般周期函数表示成简单周期函数和
    [傅里叶变换及其应用学习笔记] 二. 周期性,三角函数表示复杂函数
  • 原文地址:https://www.cnblogs.com/doitjust/p/13307738.html
Copyright © 2020-2023  润新知