RT-Thread多线程导致的临界区问题

临界资源是指一次仅允许一个线程使用的共享资源。不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地对它们进行访问。每个线程中访问临界资源的那段代码称为临
界区( Critical Section),每次只准许一个线程进入临界区,进入后不允许其他线程进入。多线程程序的开发方式不同于裸机程序,多个线程在宏观上是并发运行的,因此使用一个共享资源是需要注意,否则就可能出现错误的运行结果。

#include 
#include 
#include "test.h"

rt_uint32_t  g_tmp;/* 定义一个全局变量*/

/*  变量分配4字节对齐 */
ALIGN(RT_ALIGN_SIZE)

/*  静态线程的 线程堆栈*/
static rt_uint8_t thread1_stack[512];
static rt_uint8_t thread2_stack[512];

/* 静态线程的 线程控制块 */
static struct rt_thread thread_test1;
static struct rt_thread thread_test2;


static void test1_thread_entry(void* parameter);
static void test2_thread_entry(void* parameter);

void demo_thread_creat(void)
{
    rt_err_t result;

    /* 创建静态线程 : 优先级 15 ,时间片 2个系统滴答 */
    result = rt_thread_init(&thread_test1,
                            "test1",
                            test1_thread_entry, RT_NULL,
                            (rt_uint8_t*)&thread1_stack[0], sizeof(thread1_stack), 16, 2);

    if (result == RT_EOK)
    {
        rt_thread_startup(&thread_test1);
    }

    /* 创建静态线程 : 优先级 16 ,时间片 1个系统滴答 */
    result = rt_thread_init(&thread_test2,
                            "test2",
                            test2_thread_entry, RT_NULL,
                            (rt_uint8_t*)&thread2_stack[0], sizeof(thread2_stack), 15, 1);

    if (result == RT_EOK)
    {
        rt_thread_startup(&thread_test2);
    }

}

void test1_thread_entry(void* parameter)
{
    rt_uint32_t i;

    g_tmp = 0;
    rt_kprintf("g_tmp=:%d \r\n", g_tmp);
    for(i=0; i<10000; i++)
    {
        g_tmp++;
    }
    rt_kprintf("g_tmp=:%d \r\n", g_tmp);
}

void test2_thread_entry(void* parameter)
{
    rt_thread_delay(100);// 1 rt_thread_delay(100);两种情况下得到两张图片

g_tmp++;
}
复制代码

RT-Thread多线程导致的临界区问题_第1张图片

结果分析:
在 test1 线程的 for 循环中我们对 i 做了 10000 次累加,如果没有其他线程的“干预”,那么全局变量 g_tmp 的值应该是 10000,现在的输出结果是 10001,这意味全局变量 g_tmp 的值被线程 2 修改过。整个程序运行过程中各个线程的状态变化是这样的: rt_application_init 中创建两个线程之后,由于 test2 线程的优先级比 test1 线程的优先级高,因此 test2 线程先运行,其线程处理函数第一句为 rt_thread_delay(1), 这会使得 test2 线程被挂起,挂起时间为 1 个时间片,在 test2 线程挂起的这段时间中, test1 线程是所有就绪态线程中优先级最高的线程,因此被内核调度运行。 在 test1 线程执行了一部分代码后, 1 个 tick 时间到, test2 线程被唤醒,从而成为所有就绪线程中优先级最高的线程,因此会被立刻调度运行, test1 线程被 test2 线程抢占, test2 线程中对全局变量 g_tmp 做累加操作,接下来 test2 线程执行完毕, test1 线程再次被调度运行,根据程序的运行结果可以看出,此时 test1 线程继续执行,但是我们并不知道此时 test1 线程大致是从什么地方在开始执行的,从最后的输出结果来看,只能得知此时test1 线程还没有执行到第二条 rt_kprintf 输出语句。最后 test1 线程再次打印全局变量 g_tmp的值,其值就应该是 10001。当 test2线程中的第一句为 rt_thread_delay(100)的时候,在 test2线程休眠的整个时间里,test1 线程都已经执行完毕,因此最后的输出结果为 10000。
从以上可以看到: 当公共资源在多个线程中公用时,如果缺乏必要的保护错误,最后的输出结果可能与预期的结果完全不同。为了解决这种问题,需要引入线程间通信机制,这就
是所谓的 IPC 机制( Inter-Process Communication)。

临界资源是指一次仅允许一个线程使用的共享资源。不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地对它们进行访问。每个线程中访问临界资源的那段代码称为临
界区( Critical Section),每次只准许一个线程进入临界区,进入后不允许其他线程进入。多线程程序的开发方式不同于裸机程序,多个线程在宏观上是并发运行的,因此使用一个共享资源是需要注意,否则就可能出现错误的运行结果。

#include 
#include 
#include "test.h"

rt_uint32_t  g_tmp;/* 定义一个全局变量*/

/*  变量分配4字节对齐 */
ALIGN(RT_ALIGN_SIZE)

/*  静态线程的 线程堆栈*/
static rt_uint8_t thread1_stack[512];
static rt_uint8_t thread2_stack[512];

/* 静态线程的 线程控制块 */
static struct rt_thread thread_test1;
static struct rt_thread thread_test2;


static void test1_thread_entry(void* parameter);
static void test2_thread_entry(void* parameter);

void demo_thread_creat(void)
{
    rt_err_t result;

    /* 创建静态线程 : 优先级 15 ,时间片 2个系统滴答 */
    result = rt_thread_init(&thread_test1,
                            "test1",
                            test1_thread_entry, RT_NULL,
                            (rt_uint8_t*)&thread1_stack[0], sizeof(thread1_stack), 16, 2);

    if (result == RT_EOK)
    {
        rt_thread_startup(&thread_test1);
    }

    /* 创建静态线程 : 优先级 16 ,时间片 1个系统滴答 */
    result = rt_thread_init(&thread_test2,
                            "test2",
                            test2_thread_entry, RT_NULL,
                            (rt_uint8_t*)&thread2_stack[0], sizeof(thread2_stack), 15, 1);

    if (result == RT_EOK)
    {
        rt_thread_startup(&thread_test2);
    }

}

void test1_thread_entry(void* parameter)
{
    rt_uint32_t i;

    g_tmp = 0;
    rt_kprintf("g_tmp=:%d \r\n", g_tmp);
    for(i=0; i<10000; i++)
    {
        g_tmp++;
    }
    rt_kprintf("g_tmp=:%d \r\n", g_tmp);
}

void test2_thread_entry(void* parameter)
{
    rt_thread_delay(100);// 1 rt_thread_delay(100);两种情况下得到两张图片

g_tmp++;
}
复制代码

RT-Thread多线程导致的临界区问题_第2张图片

结果分析:
在 test1 线程的 for 循环中我们对 i 做了 10000 次累加,如果没有其他线程的“干预”,那么全局变量 g_tmp 的值应该是 10000,现在的输出结果是 10001,这意味全局变量 g_tmp 的值被线程 2 修改过。整个程序运行过程中各个线程的状态变化是这样的: rt_application_init 中创建两个线程之后,由于 test2 线程的优先级比 test1 线程的优先级高,因此 test2 线程先运行,其线程处理函数第一句为 rt_thread_delay(1), 这会使得 test2 线程被挂起,挂起时间为 1 个时间片,在 test2 线程挂起的这段时间中, test1 线程是所有就绪态线程中优先级最高的线程,因此被内核调度运行。 在 test1 线程执行了一部分代码后, 1 个 tick 时间到, test2 线程被唤醒,从而成为所有就绪线程中优先级最高的线程,因此会被立刻调度运行, test1 线程被 test2 线程抢占, test2 线程中对全局变量 g_tmp 做累加操作,接下来 test2 线程执行完毕, test1 线程再次被调度运行,根据程序的运行结果可以看出,此时 test1 线程继续执行,但是我们并不知道此时 test1 线程大致是从什么地方在开始执行的,从最后的输出结果来看,只能得知此时test1 线程还没有执行到第二条 rt_kprintf 输出语句。最后 test1 线程再次打印全局变量 g_tmp的值,其值就应该是 10001。当 test2线程中的第一句为 rt_thread_delay(100)的时候,在 test2线程休眠的整个时间里,test1 线程都已经执行完毕,因此最后的输出结果为 10000。
从以上可以看到: 当公共资源在多个线程中公用时,如果缺乏必要的保护错误,最后的输出结果可能与预期的结果完全不同。为了解决这种问题,需要引入线程间通信机制,这就
是所谓的 IPC 机制( Inter-Process Communication)。

你可能感兴趣的:(RT-thread)