RT_thread 2(线程同步)

在多线程系统中,多个线程之间需要“协调”,就像你和同事们⼀起合作⼯作。RT-Thread 提供了
信号量、互斥量和事件集来帮助线程间进⾏同步。
==信号量(Semaphore==):就像⻋站发⻋信号,只有收到信号的线程才能开始执⾏任务。
==互斥量(Mutex==):确保多个线程不会同时访问同⼀资源,避免混乱。⽐如,打印机只能同时被
⼀个⼈使⽤,
==互斥量== :相当于⼀把钥匙,谁拿到钥匙谁就可以⽤打印机。
==事件集==:允许线程在多个事件触发时进⾏操作。就像你等着多个快递到达,可以选择全部到⻬
再处理,或者⼀个到达就处理⼀个。

==**线程间通信** ==
线程之间需要交换信息,RT-Thread 提供了邮箱和消息队列来实现。邮箱就像是⼀个固定容量的
收件箱,⽽消息队列是⼀个可伸缩的队列,消息可以有不同的⻓度。邮箱更⾼效,但消息队列更
加灵活。


===信号量(Semaphore) ==
信号量可以理解为电影院的座位数(资源数量)。当所有座位都满了,新的观众(线程)就需要等
前⾯的观众离开(释放资源)才能进⼊。当有空位时,观众可以进⼊。信号量通过控制资源的数
量来管理多个线程对资源的访问。

==信号量的⼯作机制 ==
信号量的核⼼思想是控制资源的访问数量。信号量的值代表了可以访问的资源数量:
获取信号量:当资源可⽤时,线程可以获取信号量,信号量的值减1。如果信号量的值为0,线
程就会等待,直到资源释放。
释放信号量:使⽤完资源后,线程释放信号量,信号量的值加1,允许其他线程访问资源

==rt_sem_t ==是 RT-Thread 操作系统中用于表示信号量的类型。信号量是一种轻型的用于解决线程间同步问题的内核对象
线程可以获取或释放它,从而达到同步或互斥的目的。通常一个信号量的计数值用于对应有效的资源数,表示剩下的可被占用的互斥资源数。
其值的含义分两种情况:
0:表示没有积累下来的 release 释放信号量操作,且有可能有在此信号量上阻塞的线程。
正值:表示有一个或多个 release 释放信号量操作。

==rt_sem_take==(获取信号量api)

⽤于获取信号量(P 操作),线程会尝试获取信号量,如果信号量的计数值为 0,线程会进⼊等待状态,直到信号量被释放或超时。

    rt_err_t

    rt_sem_take(rt_sem_t sem, rt_int32_t time);

参数说明:

sem

:信号量的句柄。

        time

:超时时间(单位为系统 tick),表⽰最⼤等待时间。如果设置为

            _FOREVER

,线程会⼀直等待。

                返回值:

                    成功:返回

                        RT_EOK

。 失败:返回 -

    RT_ETIMEOUT
   rt_sem_release
    ⽤于释放信号量(V 操作),增加信号量的计数值,并唤醒等待该信号量的线程。
     rt_err_t rt_sem_release(rt_sem_t sem); 参数说明:
      sem :信号量的句柄。 返回值: 
      成功:返回 RT_EOK 。
      失败:返回相应的错误代码

(超时)或其他错误代码


==`rt_sem_control`== 
是 RT-Thread 操作系统中的一个函数,用于控制信号量的行为。通过这个函数,你可以获取或设置信号量的当前值,重置信号量等。

以下是 `rt_sem_control` 函数的原型:

        `c复制代码  rt_err_t rt_sem_control(rt_sem_t sem, int cmd, void *arg);`
    

### 参数说明

1. **sem**: 要控制的 `rt_sem_t` 类型的信号量句柄。
2. **cmd**: 控制命令,可以是以下几种之一:
    - `RT_IPC_CMD_RESET`: 重置信号量,将其值设置为初始值(通常为0)。
    - `RT_IPC_CMD_GET_VALUE`: 获取当前信号量的值。
    - `RT_IPC_CMD_SET_VALUE`: 设置信号量的值。
3. **arg**: 指向一个变量的指针,根据不同的命令,该变量用于存储或设置信号量的值。

### 返回值

- 成功时返回 `RT_EOK`。
- 失败时返回相应的错误码。


在 RT-Thread 中,信号量(semaphore)是⼀种常⻅的同步机制,主要⽤于任务之间的
同步与互斥。它可以通过信号量的计数机制来协调多个线程的运⾏,确保资源的安全访
问。信号量的开发场景通常有以下⼏种:
==1. 任务同步 

多个任务之间需要在某些时间点进⾏同步时,可以使⽤信号量。信号量可以⽤来实现线程
间的等待和通知机制。例如,当⼀个任务完成某项⼯作后,发送⼀个信号量,通知等待该
信号量的任务继续执⾏。
应⽤场景⽰例:
⼀个任务处理传感器数据,处理完成后通知另⼀个任务进⾏数据发送。
⼀个任务等待外部事件(如外部中断)发⽣,中断发⽣后释放信号量,任务获取信号
量后继续执⾏。
===2. 任务互斥 

当多个任务需要访问共享资源时,可以使⽤信号量来实现互斥访问。互斥信号量
(Mutex)可以保证同⼀时刻只有⼀个任务能够访问共享资源,避免出现竞争条件和数据
不⼀致的情况。
应⽤场景⽰例:
多个任务同时访问同⼀个硬件外设(如串⼝、I2C 总线)时,通过信号量保证同⼀时刻
只有⼀个任务访问外设,防⽌访问冲突。
多个任务需要访问同⼀块内存数据时,使⽤信号量确保数据访问的独占性。
==3. 事件触发 

某些外部事件发⽣后,需要通知任务执⾏相应的处理逻辑。这种场景下,外部中断或事件
处理函数可以通过释放信号量来通知任务进⾏处理。任务会等待信号量,当信号量被释放
时,任务被唤醒并开始执⾏。
应⽤场景⽰例:
⼀个任务等待按键事件,当按键被按下时,中断处理程序释放信号量,任务获取信号
量并执⾏按键响应逻辑。
⽹络模块等待数据接收事件,当⽹络数据到达时,触发信号量,通知任务进⾏数据处
理。
==4. 限量资源管理 

信号量可以⽤来管理有限的资源,信号量的计数值可以表⽰可⽤资源的数量。当⼀个任务
获取到资源时,信号量的计数值减少;当任务释放资源时,信号量的计数值增加。如果信
号量的计数值为 0,则任务必须等待资源可⽤。
应⽤场景⽰例:
⼀个系统有多个任务需要访问⼀个有限的资源池(如线程池、内存池),信号量可以表
⽰可⽤资源的数量,确保任务只能在资源可⽤时进⾏访问。
限制并发访问的数量,⽐如同⼀时刻只能有固定数量的任务访问某个服务

实验1:
![[Pasted image 20250120155814.png]]
==实验结果分析:==
当代码运行时由于两个线程的优先级相等 所以会同时进入但是线程1由于delay(100)才获取信号量所以会挂起 如图所示线程2进入获取一个座位;
然后delay20s后释放信号量 线程1继续运行;
==代码:==
```
rt_sem_t sem = RT_NULL;

void thread_entry1(void *parameter)
{
    rt_thread_delay(100);
    rt_kprintf("线程1: 等待进入电影院\n");
    rt_sem_take(sem, RT_WAITING_FOREVER);
    rt_kprintf("线程1: 进入电影院!\n");
}
// 尝试获取信号量
// 线程2:离开电影院,释放信号量
void thread_entry2(void *parameter)
{
    rt_sem_take(sem, RT_WAITING_FOREVER);
    rt_kprintf("线程2: 进入电影院,获取一个座位\n");
    rt_thread_delay(20000);
    rt_kprintf("线程2: 离开电影院,释放一个座位\n");
    rt_sem_release(sem);
    // 释放信号量
}
int main(void)
{
    // 初始化信号量,初始值为1,表示电影院有1个座位
    sem = rt_sem_create("sem", 1, RT_IPC_FLAG_PRIO);
    // 创//建两个线程
    rt_thread_t tid1 = rt_thread_create("t1", thread_entry1, RT_NULL, 1024, 25, 10);

    // 解释:
    rt_thread_t tid2 = rt_thread_create("t2", thread_entry2, RT_NULL, 1024, 25, 10);
    // 启动线程
    rt_thread_startup(tid1);
    rt_thread_startup(tid2);
    return 0;
}

```

实验2:
```
rt_sem_t sem = RT_NULL;

  

void thread_entry1(void *parameter)

{

    rt_kprintf("线程1: 等待进入电影院,但是没有座位\n");

    rt_sem_take(sem, RT_WAITING_FOREVER); // 等待信号量

    rt_kprintf("线程1: 进入电影院有座位!\n");

}

  

void thread_entry2(void *parameter)

{

    rt_kprintf("线程2: 进入电影院,打扫卫生\n");

    rt_thread_delay(10000);

    rt_sem_release(sem); // 释放信号量

    rt_kprintf("线程2: 打扫完毕有座位\n");

}

  

int main(void)

{

    // 初始化信号量,初始值为0,表示电影院有0个座位

    sem = rt_sem_create("sem", 0, RT_IPC_FLAG_PRIO);

    // 创建两个线程并释放

    rt_thread_t tid1 = rt_thread_create("t1", thread_entry1, RT_NULL, 1024, 25, 10);

    rt_thread_t tid2 = rt_thread_create("t2", thread_entry2, RT_NULL, 1024, 25, 10);

  

    if (tid1 != RT_NULL)

    {

        rt_thread_startup(tid1);

        rt_kprintf("线程1启动成功! \n");

    }

    else

    {

        rt_kprintf("线程1启动失败! \n");

    }

  

    if (tid2 != RT_NULL)

    {

        rt_thread_startup(tid2);

  

        rt_kprintf("线程2启动成功! \n");

    }

    else

    {

        rt_kprintf("线程2启动失败! \n");

    }

}
```
![[Pasted image 20250120165057.png]]
同步信号量 必须等线程二执行完毕才能执行线程一


===2. 任务互斥 实验3
```
rt_sem_t sem = RT_NULL;

rt_int16_t count;

rt_int16_t max_count = 5;

  

void thread_entry1(void *parameter)

{

    while (1)

    {

        rt_sem_take(sem, RT_WAITING_FOREVER); // 等待信号量

  

        rt_kprintf("线程1: 进入电影院,有%d个座位\n", count);

        count++;

        rt_kprintf("线程1: 打扫电影院,增加%d个座位\n", count);

  

        rt_thread_delay(100);

        rt_sem_release(sem); // 释放信号量

  

        rt_thread_delay(500); // 让出cpu给其他线程

    }

}

  

void thread_entry2(void *parameter)

{

    while (1)

    {

        rt_sem_take(sem, RT_WAITING_FOREVER); // 等待信号量

  

        rt_kprintf("线程2: 进入电影院,有%d个座位\n", count);

        count++;

        rt_kprintf("线程2: 打扫电影院,增加%d个座位\n", count);

        rt_thread_delay(100);

        rt_sem_release(sem);  // 释放信号量

        rt_thread_delay(500); // 让出cpu给其他线程

    }

}

  

int main(void)

{

    // 初始化信号量,初始值为0,表示电影院有0个座位

    sem = rt_sem_create("sem", 1, RT_IPC_FLAG_PRIO);

    // 创建两个线程并释放

    rt_thread_t tid1 = rt_thread_create("t1", thread_entry1, RT_NULL, 1024, 25, 10);

    rt_thread_t tid2 = rt_thread_create("t2", thread_entry2, RT_NULL, 1024, 25, 10);

  

    if (tid1 != RT_NULL)

    {

        rt_thread_startup(tid1);

        rt_kprintf("线程1启动成功! \n");

    }

    else

    {

        rt_kprintf("线程1启动失败! \n");

    }

  

    if (tid2 != RT_NULL)

    {

        rt_thread_startup(tid2);

  

        rt_kprintf("线程2启动成功! \n");

    }

    else

    {

        rt_kprintf("线程2启动失败! \n");

    }

}
```
全局变量必须加互锁;
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值