信号量
信号量可以看作是用于管理临界资源的管理员,信号量会统计临界资源的数量,从而控制线程的访问,如果信号量数量为0,则禁止线程访问,如果大于0,则允许线程访问
1.信号量工作机制
信号量是一种轻型的用于解决线程间同步问题的内核对象,线程可以获取和释放它,从而达到同步或者互斥的目的
每个信号量对象都有一个信号量值和一个线程等待队列,信号量值用于统计资源数目,当信号量值为0时,申请信号量的线程就会被挂起在该信号量的等待队列上,等待可用的信号量实例
信号量的值最大为65535
信号量对象从rt_ipc_object中派生,由ipc容器管理
信号量对象代码:
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.信号量管理方式
-
对一个信号量的操作包含:创建 / 初始化信号量、获取信号量、释放信号量、删除 / 脱离信号量。
-
创建动态信号量
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_FIFO(先进先出)方式时线程等待队列将按照先进先出的 方式进行等待,即先到来的线程先获得资源
选择RT_IPC_FLAG_PRIO(优先级等待)方式时,等待队列将按照优先级高低对 线程进行排队,优先级高的先获得资源
- 删除动态信号量:
调用该函数删除信号量时,如果有线程在等待该信号量,那么删除操作会先唤醒等待在该信号量上的线程,然后再释放信号量的内存资源rt_err_t rt_sem_delete(rt_sem_t sem);
- 创建和删除静态信号量
对于静态信号量对象,它的内存空间在编译时期就被编译器分配出来,放在读写数据段或未初始化数据段上,此时使用信号量就不再需要使用 rt_sem_create 接口来创建它,而只需在使用前对它进行初始化即可。
参数解释:sem信号量对象句柄 name信号量名称 value信号量初始值rt_err_t rt_sem_init(rt_sem_t sem, const char *name, rt_uint32_t value, rt_uint8_t flag)
flag信号量标志,与动态相同
- 创建和删除静态信号量
- 线程获取信号量
- 线程通过获取信号量来获得信号量资源实例,当信号量值大于零时,线程将获得信号量,并且相应的信号量值会减 1,获取信号量使用下面的函数接口:
sem为线程需要获取的信号量句柄,time为线程获得信号量的等待时间,如果再time内获取不到信号量,线程将超时返回rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time);
- 线程无等待获取信号量
当用户不想在申请的信号量上挂起线程进行等待时,可以使用无等待方式获取信号量,无等待获取信号量使用下面的函数接口:
调用该函数获取信号量时,若是当前申请的信号量资源实例不可用时,函数将直接返回 - RT_ETIMEOUT。rt_err_t rt_sem_trytake(rt_sem_t sem);
- 释放信号量
释放信号量可以唤醒挂起在该信号量上的线程。释放信号量使用下面的函数接口:
该函数说是释放不如理解为赋予,调用该函数后信号量的值就会增加rt_err_t rt_sem_release(rt_sem_t sem);
- 线程通过获取信号量来获得信号量资源实例,当信号量值大于零时,线程将获得信号量,并且相应的信号量值会减 1,获取信号量使用下面的函数接口:
3.代码示例
代码创建了一个信号量,信号量值初始为0,两个线程,线程1先释放10个信号量值,当线程1释放10信号量值后,线程2再获取信号量10次
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-08-28 RT-Thread first version
*/
#include <rtthread.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#define THREAD_PRIORITY 25
#define THREAD_TIMESLICE 5
int count=0;
/* 指向信号量的指针 */
static rt_sem_t dynamic_sem = RT_NULL;
static char thread1_stack[1024];
static struct rt_thread thread1;
static void rt_thread1_entry(void *parameter)
{
while(1)
{
if(count < 100)
{
count++;
}
else
return;
/* count 每计数 10 次,就释放一次信号量 */
if(0 == (count % 10))
{
rt_sem_release(dynamic_sem);
rt_kprintf("t1 release a dynamic semaphore.\n");
rt_kprintf("signal value %d\n",dynamic_sem->value);
}
rt_thread_mdelay(50);
}
}
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 自加的操作 */
if(count==100)
{
result = rt_sem_take(dynamic_sem, RT_WAITING_FOREVER);
if(result==RT_EOK)
{
number++;
rt_kprintf("t2 take a dynamic semaphore. number = %d\n" ,number);
}
}
rt_thread_mdelay(100);
}
}
int main(void)
{
/* 创建一个动态信号量,初始值是 0 */
dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_PRIO);
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 RT_EOK;
}