RT-Thread信号量

举报
yd_274589494 发表于 2024/05/26 08:19:37 2024/05/26
【摘要】 @TOC 前言本篇文章来给大家讲解RT-Thread中的信号量,那么这篇文章将会告诉大家信号量是什么,以及如何去使用信号量。 一、信号量的概念在 RT-Thread 中,信号量是一种常用的线程同步和互斥机制,用于控制对共享资源的访问。RT-Thread 提供了丰富的信号量操作函数,可以方便地在多线程环境下实现同步和互斥。 二、信号量和队列的对比在RT-Thread中,信号量和队列都是用于线程...

@TOC


前言

本篇文章来给大家讲解RT-Thread中的信号量,那么这篇文章将会告诉大家信号量是什么,以及如何去使用信号量。

一、信号量的概念

在 RT-Thread 中,信号量是一种常用的线程同步和互斥机制,用于控制对共享资源的访问。RT-Thread 提供了丰富的信号量操作函数,可以方便地在多线程环境下实现同步和互斥。

在这里插入图片描述

二、信号量和队列的对比

在RT-Thread中,信号量和队列都是用于线程间通信和同步的机制,但它们在功能和应用上有一些不同。

  1. 信号量(Semaphore)

    • 信号量是一种计数器,用于控制多个线程对共享资源的访问。它通常用于限制同时访问某一资源的线程数量。
    • RT-Thread中的信号量可以是二进制信号量(Binary Semaphore)或计数信号量(Counting Semaphore)。二进制信号量的计数器只有0和1两个状态,通常用于实现互斥访问,而计数信号量的计数器可以是任意正整数,用于实现资源的共享和访问控制。
    • 使用信号量时,线程可以通过P操作(等待信号量)和V操作(释放信号量)来对信号量进行操作,从而实现对共享资源的访问控制。
  2. 队列(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创建信号量函数

这两个函数都用于创建和初始化信号量,但它们在功能上有一些区别。

  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函数用于创建一个新的信号量,并初始化其属性和计数器的初始值。
      • 返回一个信号量的句柄,该句柄可以用于后续对该信号量进行操作,如获取、释放等。
  2. 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删除信号量函数

这两个函数都与信号量的销毁和释放相关,但在功能上有一些区别。

  1. 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函数用于删除并释放一个信号量,释放其所占用的资源。
      • 删除信号量后,所有等待该信号量的线程都将被唤醒,并且可能会返回一个错误码,表示信号量已被删除。
      • 通常在不再需要使用某个信号量时,可以调用该函数来释放资源,以防止资源泄漏。
  2. 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获取信号量函数

这两个函数都是用于获取(获取)信号量的操作,但在行为上有所不同。

  1. 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参数指定了等待信号量的最大超时时间,可以设置为无限等待或者一段固定的时间。
  2. 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;
}

总结

本篇文章主要讲解了信号量的概念以及使用方法,大家可以学习完成后练习一下信号量的基本使用。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。