rtt——线程同步

freertos

线程同步的概念

在操作系统中完成一项任务,可能往往需要多个线程协同完
例如我们显示传感器检测的数据,需要两个线程,一个接收线程,接收传感器数据,并将数据写入共享内存中;另一个显示线程,从共享内存中读取数据并显示出来

如果共享内存不是排他性(一个访问时另外的线程不可以访问),可能引起数据一致性的问题,在显示线程读取的时候,接收线程还没有完全完成数据的存放,可能会导致数据显示的不完整性;
所以要求多个线程访问同一个共享空间时候必须是互斥的(同一时间只能一个访问),在当前线程对共享内存操作完成后,才允许下一个线程对该共享内存做处理

线程的同步,其实就是线程按照一定的先后顺序有序的执行
线程同步是指多个线程通过特定的机制(信号量、互斥量、事件对象、临界区)来控制线程执行的顺序
,如果没有线程同步,那么线程之间将会无序的

多个线程访问同一块区域,这块区域就是临界区线程互斥就是对临界区资源访问的排他性,即任何时刻只允许一个线程访问/使用,其他线程必须等待资源。
通过线程互斥实现了线程的有序,所以线程互斥也可以称为特殊的线程同步

线程的同步有很多种方式,其核心就是在访问临界区的时候只允许一个线程运行。

在内核中,也有很多关闭/进入临界区的方法(下面两个方法少用):

  • 关闭硬件中断:函数为rt_hw_interrput_disable,表示关闭中断,rt_hw_interrupt_enable()重新使能中断。线程的调度就是基于线程中断的,所以只要关闭中断,则无法进行线程调度;所以保证了对临界区访问的排他性

  • 禁止调度器:rt_enter_critical()禁止调度器;rt_exit_critical()开启调度器

信号量介绍

信号量本质就是一个变量加一个队列;变量是代表可能资源、队列即等待资源的线程进行排队

信号量对象的定义:

/**
* Semaphore structure
*/
struct rt_semaphore
{
struct rt_ipc_object parent; /**< IPC变量,IPC为进程间通信 */

rt_uint16_t value; /**< 资源数量 */
rt_uint16_t reserved; /**< 保留,满足32位对齐 */
};
typedef struct rt_semaphore *rt_sem_t;

/**
* Base structure of IPC object
*/
struct rt_ipc_object
{
struct rt_object parent; /**< 已成内核对象*/

rt_list_t suspend_thread; /**< 队列,等待信号量的线程在此排队 */
};

信号量应用场景

线程与线程同步

线程同步是信号量最常见的一类应用。
使用信号量进行两个线程之间的同步,信号量的初始化为0,表示具备0个信号量资源示例;当线程尝试获取该资源的时候,将直接在这个信号量上等待
当持有信号量的线程完成它对共享资源的操作时候,释放这个信号量,那么信号量的数量递增;
然后把等待这个资源的线程唤醒,让他执行对应的操作,完成后再释放信号量,通知下一个线程

中断与线程同步

与线程与线程同步类似
我们将信号量初始化为0,当中断相应的时候,释放信号量;
当中断没有相应的时候,线程尝试获取资源的时候,因为这个信号量为0,则只能挂起等待信号量

资源计数

信号量可以作为一个递增或者递减的计数器;信号量是非负的值
资源计数适合用于线程间处理速度不匹配的场合;信号量可以作为一个线程的完成次数的计数器,当另一线程调用资源的时候,就可以知道上一线程的执行次数;实现一次调用可以处理多个信号量资源

信号量的工作机制

多个线程访问有限的共享资源受,为了保证访问的有序进行,我们可以使用信号量
当一线程要访问共享资源时,必须先获取信号量的值,如果信号量的值为非0,则线程可以访问共享资源,否则线程必须在信号量的队列上等待

RT-thread提供了多个信号量接口

创建信号量

创建信号量的方法有动态和静态方法

动态创建信号量

rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag);

初始化的时候需要设置信号量标志,分别为RT_IPC_FLAG_FIFO、RT_IPC_FLAG_PRIO;
RT_IPC_FLAG_FIFO为线程在队列中按照先进先出的方式排队、RT_IPC_FLAG_PRIO为线程在队列中按照优先级排序
建议使用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_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time);
rt_err_t rt_sem_trytake(rt_sem_t sem);

rt_sem_trytake即为将rt_sem_take的等待事件设置为0

rt_err_t rt_sem_trytake(rt_sem_t sem)
{
    return rt_sem_take(sem, 0);
}

rt_sem_take调用时候,先判断当前信号量是否大于0,如果大于0则表示信号量获取成功;否则,信号量不可用,此线程根据函数的time参数决定是否等待,以及等待时长

  • time=0时,线程不等待,在RT-thread中宏定义RT_WAITING_NO与0同意

  • time>0时,线程等待time时间

  • time<0时,线程永久等待,直到信号量可用,可以使用RT_WAITING_FOREVER代替

释放信号量

rt_err_t rt_sem_release(rt_sem_t sem);

使用这个函数释放信号量的时候,如果在信号量队列中有线程在排队,则唤醒等待的第一个线程;否则将信号量的值加1

删除信号量

rt_err_t rt_sem_detach(rt_sem_t sem);
rt_err_t rt_sem_delete(rt_sem_t sem);

相关文章

rtt——线程间通信(互斥量)

互斥量的概念 在 RT-Thread 中,互斥量(Mutex,互斥锁)用于确保共享资源在任意时刻只被一个线程访问 - **确保共享资源的互斥访问**:互斥量用于保护对共享资源的访问,确保在任意时刻只有一个线程可以访问该资源;避免多个线程同时修改共享资源而导致的数据不一致或竞态条件 - 实现临界区保护:互斥量通常用于实现临界区保护,将对共享资源的访问限制在临界区内。**只有获取了互斥量的线程...

freertos

rtt——线程间通信(消息队列)

**消息队列是一种线程间的通信方式,是邮箱的扩展** 消息队列的工作机制 消息队列能够接收来自线程或中断服务例程中**不固定长度的消息**,**并把消息缓存在自己的内存空间中**。其他线程也能够从消息队列中读取相应的消息,而当消息队列为空时,可挂起读取线程。当有新的消息到达时,挂起的线程将被唤醒为接收并处理消息 **消息队列是一种异步的通信方式** 通常将先进入消息队列的消息先传给线程...

freertos

rtt——线程间的通信(邮箱)

邮箱 **邮箱是实时操作系统中的一种典型的线程间的通信方式** **邮箱的通信方式除了可以一对一,也可以一对多通信,或者多对多通信** 邮箱的工作机制 在RT-thread操作系统中,邮箱是开销较低、效率较高的一种线程间通信方式。 **邮箱中的每一封邮件的容量固定为4字节(刚好容下一个32位系统的指针)** 线程或中断服务程序把一封长度为4字节的邮件发送到指定邮箱中,而其他一...

freertos