RT_Thread_信号量

本文介绍了信号量在实时操作系统中的创建、获取和释放,以及其在生产者消费者问题中的应用。生产者线程在获取空位后写入数据并增加数据计数,消费者线程则在获取满位后读取数据并释放空位,通过信号量协调了两者对共享资源的访问。

1、信号量

1.1、创建和删除信号量

静态信号量

rt_err_t rt_sem_init(rt_sem_t       sem,
                    const char     *name,
                    rt_uint32_t    value,
                    rt_uint8_t     flag)

只看函数声明,信号量的初始值value是32位,但实际是16位的:

 

参数描述
sem信号量对象的句柄
name信号量名称
value信号量初始值
flag信号量标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO
返回——
RT_EOK初始化成功
rt_err_t rt_sem_detach(rt_sem_t sem);

内核先唤醒所有挂在该信号量等待队列上的线程,然后将该信号量从内核对象管理器中脱离。

参数描述
sem信号量对象的句柄
返回——
RT_EOK脱离成功

动态信号量

 rt_sem_t rt_sem_create(const char *name,
                        rt_uint32_t value,
                        rt_uint8_t flag);
参数描述
name信号量名称
value信号量初始值
flag

信号量标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO;

建议采用 RT_IPC_FLAG_PRIO,即确保线程的实时性。

返回——
RT_NULL创建失败
信号量的控制块指针创建成功
rt_err_t rt_sem_delete(rt_sem_t sem);

唤醒等待在该信号量上的线程,_ipc_list_resume_all(&(sem->parent.suspend_thread));。 

参数描述
semrt_sem_create() 创建的信号量对象
返回——
RT_EOK删除成功

1.2、获取信号量

信号量值大于零时,线程将获得信号量,并且相应的信号量值会减 1;

信号量的值等于零,说明当前信号量资源实例不可用,申请该信号量的线程将根据 time 参数的情况选择直接返回、或挂起等待一段时间、或永久等待;

rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time);
参数描述
sem信号量对象的句柄
time指定的等待时间,单位是操作系统时钟节拍(OS Tick)
返回——
RT_EOK成功获得信号量
-RT_ETIMEOUT超时依然未获得信号量
-RT_ERROR其他错误

rt_sem_trytake是不等待获取信号量,与 rt_sem_take(sem, RT_WAITING_NO) 的作用相同 

rt_err_t rt_sem_trytake(rt_sem_t sem);
参数描述
sem信号量对象的句柄
返回——
RT_EOK成功获得信号量
-RT_ETIMEOUT获取失败

1.3、释放信号量

rt_err_t rt_sem_release(rt_sem_t sem);
参数描述
sem信号量对象的句柄
返回——
RT_EOK成功释放信号量

2、生产者和消费者

2.1、描述

有一块共享的内存空间,生产者负责写入数据,消费者负责读出数据。

对于生产者线程:有空位的时候,才能写入数据,写完数据后标记数据的数量加1;

对于消费者线程:有数据的时候,才能读出数据,读出数据后标记数据的空位加1;

2.2、代码

/* 生产者线程入口 */
void producer_thread_entry(void *parameter)
{
    int cnt = 0;
    while (cnt < 10)
    {
        /* 获取一个空位 */
        rt_sem_take(&sem_empty, RT_WAITING_FOREVER);

        /* 修改array内容,上锁 */
        rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
        array[set % MAXSEM] = cnt + 1;
        rt_kprintf("the producer generates a number: %d\n", array[set % MAXSEM]);
        set++;
        rt_sem_release(&sem_lock);

        /* 发布一个满位 */
        rt_sem_release(&sem_full);
        cnt++;

        rt_thread_mdelay(20);
    }
    rt_kprintf("the producer exit!\n");
}

/* 消费者线程入口 */
void consumer_thread_entry(void *parameter)
{
    rt_uint32_t sum = 0;

    while (1)
    {
        /* 获取一个满位 */
        rt_sem_take(&sem_full, RT_WAITING_FOREVER);

        /* 临界区,上锁进行操作 */
        rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
        sum += array[get % MAXSEM];
        rt_kprintf("the consumer[%d] get a number: %d\n", (get % MAXSEM), array[get % MAXSEM]);
        get++;
        rt_sem_release(&sem_lock);

        /* 释放一个空位 */
        rt_sem_release(&sem_empty);

        /* 生产者生产到10个数目,停止,消费者线程相应停止 */
        if (get == 10) break;

        rt_thread_mdelay(50);
    }

    rt_kprintf("the consumer sum is: %d\n", sum);
    rt_kprintf("the consumer exit!\n");
}
RT-Thread 操作系统中,信号量(semaphore)和定时器(timer)的复用机制可以通过任务调度、资源同步和事件触发等机制实现高效的并发控制。RT-Thread 提供了完整的线程间通信机制,包括信号量、互斥锁、事件集等,同时也支持硬件定时器和软件定时器的复用。 ### 信号量的使用 在 RT-Thread 中,信号量用于线程间的同步和资源管理。通过 `rt_sem_create` 创建一个信号量,并通过 `rt_sem_take` 和 `rt_sem_release` 实现 P/V 操作。在多线程环境中,信号量可以确保多个任务对共享资源的有序访问。 示例代码如下: ```c #include <rtthread.h> #define THREAD_PRIORITY 25 #define THREAD_STACK_SIZE 1024 #define THREAD_TIMESLICE 5 rt_sem_t sem = RT_NULL; /* 线程入口 */ static void thread_entry(void* parameter) { rt_err_t result; /* 获取信号量 */ result = rt_sem_take(sem, RT_WAITING_FOREVER); if (result != RT_EOK) { rt_kprintf("线程 %s 获取信号量失败\n", (char*)parameter); return; } rt_kprintf("线程 %s 正在执行\n", (char*)parameter); /* 释放信号量 */ rt_sem_release(sem); } int main(void) { rt_thread_t tid1, tid2; rt_thread_t tid3, tid4; /* 创建信号量 */ sem = rt_sem_create("sem", 1, RT_IPC_FLAG_FIFO); if (sem == RT_NULL) { rt_kprintf("创建信号量失败\n"); return -1; } /* 创建多个线程 */ tid1 = rt_thread_create("thread1", thread_entry, "thread1", THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); tid2 = rt_thread_create("thread2", thread_entry, "thread2", THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); tid3 = rt_thread_create("thread3", thread_entry, "thread3", THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); tid4 = rt_thread_create("thread4", thread_entry, "thread4", THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); /* 启动线程 */ if (tid1) rt_thread_startup(tid1); if (tid2) rt_thread_startup(tid2); if (tid3) rt_thread_startup(tid3); if (tid4) rt_thread_startup(tid4); return 0; } ``` ### 定时器的使用与复用 RT-Thread 支持两种类型的定时器:单次定时器(one-shot)和周期定时器(periodic)。定时器可以通过 `rt_timer_create` 创建,并通过 `rt_timer_start` 启动。定时器的回调函数可以在指定时间后被调用,实现任务调度或事件触发。 示例代码如下: ```c #include <rtthread.h> static rt_timer_t timer1 = RT_NULL; static rt_timer_t timer2 = RT_NULL; /* 定时器回调函数 */ void timer1_timeout(void* parameter) { rt_kprintf("定时器1超时,参数为:%s\n", (char*)parameter); } void timer2_timeout(void* parameter) { rt_kprintf("定时器2超时,参数为:%s\n", (char*)parameter); } int main(void) { /* 创建定时器 */ timer1 = rt_timer_create("timer1", timer1_timeout, "Timer1", 10, RT_TIMER_FLAG_PERIODIC); timer2 = rt_timer_create("timer2", timer2_timeout, "Timer2", 20, RT_TIMER_FLAG_PERIODIC); /* 启动定时器 */ if (timer1) rt_timer_start(timer1); if (timer2) rt_timer_start(timer2); return 0; } ``` ### 复用机制的实现 在 RT-Thread 中,信号量和定时器的复用机制主要体现在以下方面: - **定时器复用**:通过在定时器回调函数中操作信号量,可以实现多个任务之间的协同调度。例如,一个定时器触发后释放信号量,等待该信号量的任务可以被唤醒并执行。 - **信号量复用**:多个线程可以共享同一个信号量,通过控制信号量的计数器实现资源的有序访问。 - **结合使用**:可以将定时器与信号量结合使用,实现延迟任务调度、超时控制等功能。例如,在定时器回调中释放信号量,使等待该信号量的任务得以继续执行[^1]。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值