RT-Thread信号量
@TOC
前言
本篇文章来给大家讲解RT-Thread中的信号量,那么这篇文章将会告诉大家信号量是什么,以及如何去使用信号量。
一、信号量的概念
在 RT-Thread 中,信号量是一种常用的线程同步和互斥机制,用于控制对共享资源的访问。RT-Thread 提供了丰富的信号量操作函数,可以方便地在多线程环境下实现同步和互斥。
二、信号量和队列的对比
在RT-Thread中,信号量和队列都是用于线程间通信和同步的机制,但它们在功能和应用上有一些不同。
-
信号量(Semaphore):
- 信号量是一种计数器,用于控制多个线程对共享资源的访问。它通常用于限制同时访问某一资源的线程数量。
- RT-Thread中的信号量可以是二进制信号量(Binary Semaphore)或计数信号量(Counting Semaphore)。二进制信号量的计数器只有0和1两个状态,通常用于实现互斥访问,而计数信号量的计数器可以是任意正整数,用于实现资源的共享和访问控制。
- 使用信号量时,线程可以通过P操作(等待信号量)和V操作(释放信号量)来对信号量进行操作,从而实现对共享资源的访问控制。
-
队列(Queue):
- 队列是一种数据结构,采用先进先出(FIFO)的方式存储和获取数据。在RT-Thread中,队列通常用于在不同线程之间传递数据,实现线程间的数据交换和通信。
- RT-Thread提供了多种类型的队列,包括消息队列、邮箱队列等。消息队列用于传递固定大小的数据块,而邮箱队列则用于传递指针或动态分配的数据块。
- 队列的操作包括入队和出队操作,线程可以将数据放入队列中或从队列中获取数据,实现数据的传递和共享。
对比:
- 相同点:信号量和队列都可以用于线程间的同步和通信,实现资源的共享和访问控制。
- 不同点:
- 功能:信号量主要用于控制对共享资源的访问,而队列主要用于在线程间传递数据。
- 数据类型:信号量是一个计数器,不存储实际的数据;而队列是存储数据的数据结构,可以传递各种类型的数据。
- 应用场景:信号量适用于控制对资源的并发访问,例如实现互斥访问或限制同时访问资源的线程数量;而队列适用于在不同线程之间传递数据,例如实现生产者-消费者模式或异步事件处理。
综上所述,信号量和队列在RT-Thread中都是重要的线程同步和通信机制,选择使用哪种取决于具体的应用场景和需求。
三、信号量的函数使用
在RT-Thread中,struct rt_semaphore结构体用于管理信号量。
struct rt_semaphore
{
struct rt_ipc_object parent; /**< inherit from ipc_object */
rt_uint16_t value; /**< value of semaphore. */
rt_uint16_t reserved; /**< reserved field */
}
下面是对struct rt_semaphore结构体成员的解释:
parent:继承自struct rt_ipc_object,用于存储信号量的IPC对象属性,如名称、类型等。
value:信号量的计数器,表示当前可用资源的数量。
reserved:保留字段,目前没有特定的用途。
3.1创建信号量函数
这两个函数都用于创建和初始化信号量,但它们在功能上有一些区别。
-
rt_sem_create
函数:rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag);
-
参数:
name
:信号量的名称,用于标识和调试。value
:信号量的初始值,表示可用资源的数量。flag
:标志位,指定信号量的行为,例如是否为自动清零等。
-
返回值:
rt_sem_t
:信号量的句柄,用于后续对信号量进行操作。
-
功能:
rt_sem_create
函数用于创建一个新的信号量,并初始化其属性和计数器的初始值。- 返回一个信号量的句柄,该句柄可以用于后续对该信号量进行操作,如获取、释放等。
-
-
rt_sem_init
函数:rt_err_t rt_sem_init(rt_sem_t sem, const char *name, rt_uint32_t value, rt_uint8_t flag);
-
参数:
sem
:要初始化的信号量的句柄。name
:信号量的名称,用于标识和调试。value
:信号量的初始值,表示可用资源的数量。flag
:标志位,指定信号量的行为,例如是否为自动清零等。
-
返回值:
rt_err_t
:初始化是否成功的错误码,若成功则返回RT_EOK
,否则返回相应的错误码。
-
功能:
rt_sem_init
函数用于初始化已经存在的信号量。- 它不会创建新的信号量,而是对已经存在的信号量进行初始化。
- 需要传入信号量的句柄作为参数。
-
这两个函数的作用都是对信号量进行初始化,但rt_sem_create
函数用于创建并初始化新的信号量,而rt_sem_init
函数用于初始化已经存在的信号量。
3.2删除信号量函数
这两个函数都与信号量的销毁和释放相关,但在功能上有一些区别。
-
rt_err_t rt_sem_delete(rt_sem_t sem)
:rt_err_t rt_sem_delete(rt_sem_t sem);
-
参数:
sem
:要删除的信号量的句柄。
-
返回值:
rt_err_t
:表示函数执行状态的错误码,若成功则返回RT_EOK
,否则返回相应的错误码。
-
功能:
rt_sem_delete
函数用于删除并释放一个信号量,释放其所占用的资源。- 删除信号量后,所有等待该信号量的线程都将被唤醒,并且可能会返回一个错误码,表示信号量已被删除。
- 通常在不再需要使用某个信号量时,可以调用该函数来释放资源,以防止资源泄漏。
-
-
rt_err_t rt_sem_detach(rt_sem_t sem)
:rt_err_t rt_sem_detach(rt_sem_t sem);
-
参数:
sem
:要分离的信号量的句柄。
-
返回值:
rt_err_t
:表示函数执行状态的错误码,若成功则返回RT_EOK
,否则返回相应的错误码。
-
功能:
rt_sem_detach
函数用于分离一个信号量,但不会立即释放其占用的资源。- 分离信号量后,它将不再可用,但资源不会立即释放,而是延迟到信号量计数器归零时释放。
- 分离信号量后,线程无法再次等待该信号量,也无法对其进行获取或释放操作。
- 通常在需要暂时停用某个信号量,但又不希望立即释放其资源时,可以调用该函数来分离信号量。
-
rt_sem_delete
函数用于立即释放信号量及其资源,而rt_sem_detach
函数用于暂时停用信号量,但延迟释放其资源。
3.3获取信号量函数
这两个函数都是用于获取(获取)信号量的操作,但在行为上有所不同。
-
rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time)
:rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time);
-
参数:
sem
:要获取的信号量的句柄。time
:等待信号量的超时时间,单位为毫秒。如果设置为RT_WAITING_FOREVER
,则表示无限等待;如果设置为RT_WAITING_NO
,则表示不等待,立即返回。
-
返回值:
rt_err_t
:表示函数执行状态的错误码,若成功获取信号量则返回RT_EOK
,否则返回相应的错误码。
-
功能:
rt_sem_take
函数用于获取(等待)一个信号量。- 如果信号量的计数器大于0,则将计数器减1,并立即返回成功。
- 如果信号量的计数器等于0,则当前线程将被阻塞,直到计数器大于0或超时。
time
参数指定了等待信号量的最大超时时间,可以设置为无限等待或者一段固定的时间。
-
-
rt_err_t rt_sem_trytake(rt_sem_t sem)
:rt_err_t rt_sem_trytake(rt_sem_t sem);
-
参数:
sem
:要尝试获取的信号量的句柄。
-
返回值:
rt_err_t
:表示函数执行状态的错误码,若成功获取信号量则返回RT_EOK
,否则返回相应的错误码。
-
功能:
rt_sem_trytake
函数用于尝试获取(非阻塞)一个信号量。- 如果信号量的计数器大于0,则将计数器减1,并立即返回成功。
- 如果信号量的计数器等于0,则立即返回失败,不会阻塞当前线程。
-
这两个函数的选择取决于应用场景和需求。如果需要线程等待直到信号量可用或超时,可以使用rt_sem_take
函数;如果希望线程尝试获取信号量但不愿意等待,可以使用rt_sem_trytake
函数。
3.4释放信号量函数
rt_err_t rt_sem_release(rt_sem_t sem)
函数用于释放(释放)一个信号量。
-
参数:
sem
:要释放的信号量的句柄。
-
返回值:
rt_err_t
:表示函数执行状态的错误码,若成功释放信号量则返回RT_EOK
,否则返回相应的错误码。
-
功能:
rt_sem_release
函数用于释放一个信号量。- 当信号量的计数器小于
RT_SEM_MAX_VALUE
时,将计数器加1。 - 如果有线程正在等待该信号量,则唤醒其中一个等待线程,并使其获取到信号量。
- 释放信号量后,其他等待该信号量的线程可以通过获取信号量继续执行。
这个函数通常与rt_sem_take
配对使用,用于释放通过rt_sem_take
获取的信号量。释放信号量后,其他等待该信号量的线程将有机会获取到信号量并继续执行。
四.信号量的基本使用
#include <rtthread.h>
#define THREAD_STACK_SIZE 512
#define THREAD_PRIORITY 20
// 定义一个全局信号量
static rt_sem_t sem;
// 共享资源
static int shared_resource = 0;
// 线程1的入口函数
static void thread1_entry(void *parameter)
{
while (1)
{
// 等待获取信号量
rt_sem_take(sem, RT_WAITING_FOREVER);
// 访问共享资源
shared_resource++;
rt_kprintf("Thread 1: Shared resource value: %d\n", shared_resource);
// 释放信号量
rt_sem_release(sem);
// 延时一段时间
rt_thread_mdelay(1000);
}
}
// 线程2的入口函数
static void thread2_entry(void *parameter)
{
while (1)
{
// 等待获取信号量
rt_sem_take(sem, RT_WAITING_FOREVER);
// 访问共享资源
shared_resource--;
rt_kprintf("Thread 2: Shared resource value: %d\n", shared_resource);
// 释放信号量
rt_sem_release(sem);
// 延时一段时间
rt_thread_mdelay(1500);
}
}
int main(void)
{
// 创建信号量,初始值为1
sem = rt_sem_create("my_semaphore", 1, RT_IPC_FLAG_FIFO);
// 创建线程1
rt_thread_t thread1 = rt_thread_create("thread1", thread1_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, 10, 0);
if (thread1 != RT_NULL)
rt_thread_startup(thread1);
// 创建线程2
rt_thread_t thread2 = rt_thread_create("thread2", thread2_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, 10, 0);
if (thread2 != RT_NULL)
rt_thread_startup(thread2);
// 启动 RT-Thread
rt_thread_startup(rt_thread_self());
return 0;
}
总结
本篇文章主要讲解了信号量的概念以及使用方法,大家可以学习完成后练习一下信号量的基本使用。
- 点赞
- 收藏
- 关注作者
评论(0)