【rtthread内核系列】第四篇:线程间同步

一、线程间同步的概念

rtthread通过线程间同步建立线程间的执行顺序,多个线程访问的同一个内存叫做临界区。rtthread提供的同步的工具

  • 信号量
  • 互斥量
  • 事件集

二、信号量

2.1 信号量概念

rtthread将信号量抽象成rt_semaphore.

struct rt_semaphore
{
    
    
    struct rt_ipc_object parent; /* 继 承 自 ipc_object 类 */
    rt_uint16_t value; /* 信 号 量 的 值 */
};
/* rt_sem_t 是 指 向 semaphore 结 构 体 的 指 针 类 型 */
typedef struct rt_semaphore* rt_sem_t;

2.2 信号量api

//创建信号量
/*
name:信号量名称
value:信号量初始值
flag:标志,可取RT_IPC_FLAG_FIFO 或RT_IPC_FLAG_PRIO
*/
rt_sem_t rt_sem_create(const char *name,
                                rt_uint32_t value,
                                rt_uint8_t flag);
                                
//删除信号量
/*
sem:信号量句柄
*/
rt_err_t rt_sem_delete(rt_sem_t sem);

//初始化信号量
/*
sem:信号量句柄
name:信号量名称
value:信号量初始值
flag:标志,可取RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO
*/
rt_err_t rt_sem_init(rt_sem_t sem,
                        const char *name,
                        rt_uint32_t value,
                        rt_uint8_t flag)
                        
//信号量脱离
/*
sem:信号量句柄
*/
rt_err_t rt_sem_detach(rt_sem_t sem);

//取得信号量
/*
sem:信号量句柄
time:等待超时时间
*/
rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time);

//无等待获取信号量
/*
sem:信号量句柄
*/
rt_err_t rt_sem_trytake(rt_sem_t sem);

//释放信号量
/*
sem:信号量句柄
*/
rt_err_t rt_sem_release(rt_sem_t sem);

2.3 信号量示例

本示例定义了两个线程,线程1释放信号量,线程2取得信号量,信号量的初始值代表资源的数目。

/*
 * 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
 */

/*
 * 程序清单:信号量例程
 *
 * 该例程创建了一个动态信号量,初始化两个线程,线程1在count每计数10次时,
 * 发送一个信号量,线程2在接收信号量后,对number进行加1操作
 */
#include <rtthread.h>

#define THREAD_PRIORITY         25
#define THREAD_TIMESLICE        5

/* 指向信号量的指针 */
static rt_sem_t dynamic_sem = RT_NULL;

ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;
static void rt_thread1_entry(void *parameter)
{
    
    
    static rt_uint8_t count = 0;

    while (1)
    {
    
    
        if (count <= 100)
        {
    
    
            count++;
        }
        else
            return;

        /* count每计数10次,就释放一次信号量 */
        if (0 == (count % 10))
        {
    
    
            rt_kprintf("thread1 release a dynamic semaphore.\n");
            rt_sem_release(dynamic_sem);
        }
    }
}

ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
static void rt_thread2_entry(void *parameter)
{
    
    
    static rt_err_t result;
    static rt_uint8_t number = 0;
    while (1)
    {
    
    
        /* 永久方式等待信号量,获取到信号量,则执行number自加的操作 */
        result = rt_sem_take(dynamic_sem, RT_WAITING_FOREVER);
        if (result != RT_EOK)
        {
    
    
            rt_kprintf("thread2 take a dynamic semaphore, failed.\n");
            rt_sem_delete(dynamic_sem);
            return;
        }
        else
        {
    
    
            number++;
            rt_kprintf("thread2 take a dynamic semaphore. number = %d\n", number);
        }
    }
}

/* 信号量示例的初始化 */
int semaphore_sample()
{
    
    
    /* 创建一个动态信号量,初始值是0 */
    dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_FIFO);
    if (dynamic_sem == RT_NULL)
    {
    
    
        rt_kprintf("create dynamic semaphore failed.\n");
        return -1;
    }
    else
    {
    
    
        rt_kprintf("create done. dynamic semaphore value = 0.\n");
    }

    rt_thread_init(&thread1,
                   "thread1",
                   rt_thread1_entry,
                   RT_NULL,
                   &thread1_stack[0],
                   sizeof(thread1_stack),
                   THREAD_PRIORITY, THREAD_TIMESLICE);
    rt_thread_startup(&thread1);

    rt_thread_init(&thread2,
                   "thread2",
                   rt_thread2_entry,
                   RT_NULL,
                   &thread2_stack[0],
                   sizeof(thread2_stack),
                   THREAD_PRIORITY - 1, THREAD_TIMESLICE);
    rt_thread_startup(&thread2);

    return 0;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(semaphore_sample, semaphore sample);

三、互斥量

3.1 互斥量概念

rtthread将互斥量抽象成rt_mutex.

struct rt_mutex
{
    
    
    struct rt_ipc_object parent; /* 继 承 自 ipc_object 类 */
    rt_uint16_t value; /* 互 斥 量 的 值 */
    rt_uint8_t original_priority; /* 持 有 线 程 的 原 始 优 先 级 */
    rt_uint8_t hold; /* 持 有 线 程 的 持 有 次 数 */
    struct rt_thread *owner; /* 当 前 拥 有 互 斥 量 的 线 程 */
};
/* rt_mutext_t 为 指 向 互 斥 量 结 构 体 的 指 针 类 型 */
typedef struct rt_mutex* rt_mutex_t;

3.2 互斥量api

//创建互斥量
/*
name:互斥量名称
flag:标志,可取RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO
*/
rt_mutex_t rt_mutex_create (const char* name, rt_uint8_t flag);

//删除互斥量
/*
mutex:互斥量句柄
*/
rt_err_t rt_mutex_delete (rt_mutex_t mutex);

//初始化互斥量
/*
mutex:互斥量句柄
name:互斥量名称
flag:标志,可取RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO
*/
rt_err_t rt_mutex_init (rt_mutex_t mutex, const char* name, rt_uint8_t flag);

//互斥量脱离
/*
mutex:互斥量句柄
*/
rt_err_t rt_mutex_detach (rt_mutex_t mutex)

//获取互斥量
/*
mutex:互斥量句柄
time:等待超时时间
*/
rt_err_t rt_mutex_take (rt_mutex_t mutex, rt_int32_t time);

//释放信号量
/*
mutex:互斥量句柄
*/
rt_err_t rt_mutex_release(rt_mutex_t mutex);

3.3 互斥量示例

/*
 * 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
 */

/*
 * 程序清单:互斥锁例程
 *
 * 互斥锁是一种保护共享资源的方法。当一个线程拥有互斥锁的时候,
 * 可以保护共享资源不被其他线程破坏。线程1对2个number分别进行加1操作
 * 线程2也会对2个number分别进行加1操作。使用互斥量保证2个number值保持一致
 */
#include <rtthread.h>

#define THREAD_PRIORITY         8
#define THREAD_TIMESLICE        5

/* 指向互斥量的指针 */
static rt_mutex_t dynamic_mutex = RT_NULL;
static rt_uint8_t number1, number2 = 0;

ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;
static void rt_thread_entry1(void *parameter)
{
    
    
    while (1)
    {
    
    
        /* 线程1获取到互斥量后,先后对number1、number2进行加1操作,然后释放互斥量 */
        rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
        number1++;
        rt_thread_mdelay(10);
        number2++;
        rt_mutex_release(dynamic_mutex);
    }
}

ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
static void rt_thread_entry2(void *parameter)
{
    
    
    while (1)
    {
    
    
        /* 线程2获取到互斥量后,检查number1、number2的值是否相同,相同则表示mutex起到了锁的作用 */
        rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
        if (number1 != number2)
        {
    
    
            rt_kprintf("not protect.number1 = %d, mumber2 = %d \n", number1, number2);
        }
        else
        {
    
    
            rt_kprintf("mutex protect ,number1 = mumber2 is %d\n", number1);
        }

        number1++;
        number2++;
        rt_mutex_release(dynamic_mutex);

        if (number1 >= 50)
            return;
    }
}

/* 互斥量示例的初始化 */
int mutex_sample(void)
{
    
    
    /* 创建一个动态互斥量 */
    dynamic_mutex = rt_mutex_create("dmutex", RT_IPC_FLAG_FIFO);
    if (dynamic_mutex == RT_NULL)
    {
    
    
        rt_kprintf("create dynamic mutex failed.\n");
        return -1;
    }

    rt_thread_init(&thread1,
                   "thread1",
                   rt_thread_entry1,
                   RT_NULL,
                   &thread1_stack[0],
                   sizeof(thread1_stack),
                   THREAD_PRIORITY, THREAD_TIMESLICE);
    rt_thread_startup(&thread1);

    rt_thread_init(&thread2,
                   "thread2",
                   rt_thread_entry2,
                   RT_NULL,
                   &thread2_stack[0],
                   sizeof(thread2_stack),
                   THREAD_PRIORITY - 1, THREAD_TIMESLICE);
    rt_thread_startup(&thread2);
    return 0;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(mutex_sample, mutex sample);

四、事件集

4.1 事件集概念

rtthread将事件集抽象成rt_event

struct rt_event
{
    
    
    struct rt_ipc_object parent; /* 继 承 自 ipc_object 类 */
    /* 事 件 集 合, 每 一 bit 表 示 1 个 事 件, bit 位 的 值 可 以 标 记 某 事 件 是 否 发 生 */
    rt_uint32_t set;
};
/* rt_event_t 是 指 向 事 件 结 构 体 的 指 针 类 型 */
typedef struct rt_event* rt_event_t;

4.2 事件集api

//创建事件集
/*
name:事件集名称
flag:标志,可取RT_IPC_FLAG_FIFO 或RT_IPC_FLAG_PRIO
*/
rt_event_t rt_event_create(const char* name, rt_uint8_t flag);

//删除事件集
/*
event:事件集句柄
*/
rt_err_t rt_event_delete(rt_event_t event);

//初始化事件集
/*
event:事件集句柄
name:事件集名称
flag:标志,可取RT_IPC_FLAG_FIFO 或RT_IPC_FLAG_PRIO
*/
rt_err_t rt_event_init(rt_event_t event, 
                            const char* name, 
                            rt_uint8_t flag);

//事件集脱离
/*
event:事件集句柄
*/
rt_err_t rt_event_detach(rt_event_t event);

//发送事件集
/*
event:事件集句柄
set:事件标志
*/
rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set);

//接收事件
/*
event:事件集句柄
set:事件标志
option:
    RT_EVENT_FLAG_OR、RT_EVENT_FLAG_AND
    /* 选 择 清 除 重 置 事 件 标 志 位 */
    RT_EVENT_FLAG_CLEAR
timeout:等待超时时间
recved:指向接收到的事件
*/
rt_err_t rt_event_recv(rt_event_t event,
                            rt_uint32_t set,
                            rt_uint8_t option,
                            rt_int32_t timeout,
                            rt_uint32_t* recved);
                            

4.3 事件集示例

/*
 * 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
 */

/*
 * 程序清单:事件例程
 *
 * 程序会初始化2个线程及初始化一个静态事件对象
 * 一个线程等待于事件对象上,以接收事件;
 * 一个线程发送事件 (事件3/事件5)
*/
#include <rtthread.h>

#define THREAD_PRIORITY      9
#define THREAD_TIMESLICE     5

#define EVENT_FLAG3 (1 << 3)
#define EVENT_FLAG5 (1 << 5)

/* 事件控制块 */
static struct rt_event event;

ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;

/* 线程1入口函数 */
static void thread1_recv_event(void *param)
{
    
    
    rt_uint32_t e;

    /* 第一次接收事件,事件3或事件5任意一个可以触发线程1,接收完后清除事件标志 */
    if (rt_event_recv(&event, (EVENT_FLAG3 | EVENT_FLAG5),
                      RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
                      RT_WAITING_FOREVER, &e) == RT_EOK)
    {
    
    
        rt_kprintf("thread1: OR recv event 0x%x\n", e);
    }

    rt_kprintf("thread1: delay 1s to prepare the second event\n");
    rt_thread_mdelay(1000);

    /* 第二次接收事件,事件3和事件5均发生时才可以触发线程1,接收完后清除事件标志 */
    if (rt_event_recv(&event, (EVENT_FLAG3 | EVENT_FLAG5),
                      RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
                      RT_WAITING_FOREVER, &e) == RT_EOK)
    {
    
    
        rt_kprintf("thread1: AND recv event 0x%x\n", e);
    }
    rt_kprintf("thread1 leave.\n");
}

ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;

/* 线程2入口 */
static void thread2_send_event(void *param)
{
    
    
    rt_kprintf("thread2: send event3\n");
    rt_event_send(&event, EVENT_FLAG3);
    rt_thread_mdelay(200);

    rt_kprintf("thread2: send event5\n");
    rt_event_send(&event, EVENT_FLAG5);
    rt_thread_mdelay(200);

    rt_kprintf("thread2: send event3\n");
    rt_event_send(&event, EVENT_FLAG3);
    rt_kprintf("thread2 leave.\n");
}

int event_sample(void)
{
    
    
    rt_err_t result;

    /* 初始化事件对象 */
    result = rt_event_init(&event, "event", RT_IPC_FLAG_FIFO);
    if (result != RT_EOK)
    {
    
    
        rt_kprintf("init event failed.\n");
        return -1;
    }

    rt_thread_init(&thread1,
                   "thread1",
                   thread1_recv_event,
                   RT_NULL,
                   &thread1_stack[0],
                   sizeof(thread1_stack),
                   THREAD_PRIORITY - 1, THREAD_TIMESLICE);
    rt_thread_startup(&thread1);

    rt_thread_init(&thread2,
                   "thread2",
                   thread2_send_event,
                   RT_NULL,
                   &thread2_stack[0],
                   sizeof(thread2_stack),
                   THREAD_PRIORITY, THREAD_TIMESLICE);
    rt_thread_startup(&thread2);

    return 0;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(event_sample, event sample);

猜你喜欢

转载自blog.csdn.net/weixin_43810563/article/details/116722090