RT-Thread线程间同步

本文介绍了在多线程环境下如何通过信号量、互斥量和事件集进行线程间同步,以避免竞态条件。详细讲解了信号量的创建、获取与释放,以及互斥量和事件集的相关函数及其在实际应用中的示例,如使用信号量控制线程的启动和关闭。
摘要由CSDN通过智能技术生成

在两个或多个线程会操作到同一个临界区资源的时候,会导致竞态的产生,需要通过线程间同步来避免出现错误,同步的方法有很多种,常使用信号量、互斥量(互斥锁)、事件集等。

一、信号量

每个信号量都有一个信号量值和线程等待队列,信号量值表示有几个线程可以操作此资源,没有一个线程使用此资源,信号量值减1,当为0时,再申请此信号量就会挂起在此信号量的等待队列上。

1、信号量相关函数
rt_err_t rt_sem_init(rt_sem_t    sem,
                     const char *name,
                     rt_uint32_t value,
                     rt_uint8_t  flag);//静态创建信号量
rt_err_t rt_sem_detach(rt_sem_t sem);//信号量脱离,对于静态创建的信号量
rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag);//动态创建信号量
rt_err_t rt_sem_delete(rt_sem_t sem);//信号量删除,对于动态创建的信号量

rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time);//获取信号量,如果信号量值为0,由time决定等待时间
rt_err_t rt_sem_trytake(rt_sem_t sem);//获取信号量,如果信号量值为0,直接返回
rt_err_t rt_sem_release(rt_sem_t sem);//释放信号量,信号量值加1
rt_err_t rt_sem_control(rt_sem_t sem, int cmd, void *arg);//信号量控制函数,使用相关命令可以复位信号量
2、信号量的创建和删除
  • 静态创建:

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);//挂起的线程排队顺序

删除时使用 rt_sem_detach(rt_sem_t sem)函数。

  • 动态创建:
rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag);//与静态创建的参数意义一样

删除时使用rt_sem_delete(rt_sem_t sem)函数。

3、信号量的获取和释放
  • 获取:

time表示当信号量值为0时要等待多久,可选RT_WAITING_FOREVER(一直等待)和(不等待,直接返回),如果给的大于0的数,则等待time时间。

每获取成功一次,信号量值减1。

rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time);
  • 释放:

释放信号量,信号量值加1。

rt_err_t rt_sem_release(rt_sem_t sem);

二、互斥量

相当于特殊的信号量——二值信号量,只能由同一线程进行释放,使用的函数与信号量差不多。

  • 相关函数:
rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag);
rt_err_t rt_mutex_detach(rt_mutex_t mutex);
rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag);
rt_err_t rt_mutex_delete(rt_mutex_t mutex);

rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time);
rt_err_t rt_mutex_release(rt_mutex_t mutex);
rt_err_t rt_mutex_control(rt_mutex_t mutex, int cmd, void *arg);

三、事件集

事件集可以实现一对多,多对多的线程间同步,事件集由一个32位的数据表示,每一位表示一个事件,所以一个事件集最多有32个事件,只能用作同步,不能进行线程间的通信。事件集的创建与信号量类似。

1、相关函数:
rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag);//静态创建事件集
rt_err_t rt_event_detach(rt_event_t event);//静态事件集脱离
rt_event_t rt_event_create(const char *name, rt_uint8_t flag);//动态创建事件集
rt_err_t rt_event_delete(rt_event_t event);//删除事件集

rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set);//向事件集发送事件,将对应位置1
rt_err_t rt_event_recv(rt_event_t   event,
                       rt_uint32_t  set,
                       rt_uint8_t   opt,
                       rt_int32_t   timeout,
                       rt_uint32_t *recved);//释放对应位的事件
rt_err_t rt_event_control(rt_event_t event, int cmd, void *arg);//事件控制函数,使用相应命令可以复位事件集
2、事件的发送和接收:
  • 发送:

set是一个32位数据,将要使用的事件对应位置1。

rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set);
  • 接收:

opt用来选择是否由多个事件决定,可选RT_EVENT_FLAG_AND和RT_EVENT_FLAG_OR,这两个必须选择一个,还可以再选择RT_EVENT_FLAG_CLEAR(接收完后将对应位置0)。

rt_err_t rt_event_recv(rt_event_t   event,//事件集控制块结构体
                       rt_uint32_t  set,//检查事件对用位是否为1
                       rt_uint8_t   opt,//选择是否由多个事件决定
                       rt_int32_t   timeout,//对应位为0是,选择等待时间,与信号量类似
                       rt_uint32_t *recved);//用来接收事件的值,如果不需要使用到则给RT_NULL

四、遇到的问题

原本是想要按键单击开启led1线程,led1灯闪烁,按键双击关闭led1线程,但是官方库函数中没有真正可以关闭线程的函数,rt_thread_suspend函数又只能自身线程调用。

查了资料后决定使用信号量来控制线程的关闭,信号量初始化为0,在按键双击触发的时候,释放信号量,led1回调函数中就会成功获取到信号量,就会调用rt_thread_suspend函数将led1线程关闭。

led1线程回调函数修改成:

/***********************************************
* @brief : LED1回调函数
* @param : parameter:需要传入的参数
* @return: void
* @date  : 2023.8.11
* @author: L
************************************************/
void LedThread1(void *parameter)
{
    while(1)
    {
        if(rt_sem_take(stop_sem, RT_WAITING_NO) == RT_EOK)
        {
            rt_thread_suspend(led1);//挂起当前线程
            rt_schedule();//开启调度
        }
        else
        {
            GPIO_ResetBits(GPIOC, GPIO_Pin_13);//灯亮
            rt_kprintf("led1 on\r\n");
            rt_thread_mdelay(500);
            GPIO_SetBits(GPIOC, GPIO_Pin_13);//灯灭
            rt_kprintf("led1 off\r\n");
        }
        rt_thread_mdelay(500);
    }
}

按键双击函数:

/***********************************************
* @brief : 按键双击任务函数
* @param : 选择是哪个按键
* @return: void
* @date  : 2023.8.11
* @author: L
************************************************/
void DoubleTick(void *parameter)
{
    switch(*(uint8_t*)parameter)
    {
        case 1://按键1
        {
            rt_sem_release(stop_sem);//释放信号量,停止led1线程
            break;
        }
        default:break;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值