Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
2024-RSOC
【2024-RSOC】DAY3线程间同步与通信
发布于 2024-07-25 13:38:36 浏览:349
订阅该版
[tocm] # DAY03 ## 一、信号量 ###1.1信号量的概念 信号量是一种计数器,用于控制多线程对共享资源的访问,分为二进制信号量或计数信号量。 ###1.2工作机制 每个信号量对象都有一个信号量值和一个线程等待队列,信号量的值对应了信号量对象的实例数目、资源数目,假如信号量值为 5,则表示共有 5 个信号量实例(资源)可以被使用,当信号量实例数目为零时,再申请该信号量的线程就会被挂起在该信号量的等待队列上,等待可用的信号量实例(资源)。 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20240724/b1d340b71dd537b8512cc4dd4d6b26b0.png) ###1.3信号量的使用 创建/初始化,获取信号量,释放信号量,删除/脱离信号量 ####1.3.1创建信号量 函数用于创建一个新的信号量,并初始化其属性和计数器的初始值。 参数: sem:信号量对象的句柄 name:信号量名称 value:信号量初始值 flag:信号量标志,取RT_IPC_FLAG_FIFO(先进先出)或者RT_IPC_PRIO(优先级等待) ```c rt_sem_t rt_sem_create(const char *name,rt_uint32_t value, rt_uint8_t flag); ``` **notice:信号量创建的句柄字符数<=8字符,否则无法识别** 返回值:成功则返回RT_EOK 调用之后,需要进行对应的删除工作 ####1.3.2信号量的删除 调用该函数,系统将删除该信号量,唤醒等待在该信号量上的线程,再释放信号量资源 参数: sem:创建的信号量对象 ```c rt_err_t rt_sem_delete(rt_sem_t sem); ``` 返回值: 删除成功:RT_EOK ####1.3.3信号量的初始化 它不会创建新的信号量,而是对已经存在的信号量进行初始化。 参数: sem:要初始化的信号量**结构体指针** name:信号量的名字 value:信号量初始值 flag:标志位 ```c rt_err_t rt_sem_init(rt_sem_t sem, const char *name, rt_uint32_t value, rt_uint8_t flag); ``` 返回值: 成功创建:RT_EOK ####1.3.4信号量脱离 分离信号量后,它将不再可用,但资源不会立即释放,而是延迟到信号量计数器归零时释放。分离信号量后,线程无法再次等待该信号量,也无法对其进行获取或释放操作。通常在需要暂时停用某个信号量,但又不希望立即释放其资源时,可以调用该函数来分离信号量。 参数: sem:要分离的句柄 ```c rt_err_t rt_sem_detach(rt_sem_t sem); ``` 返回值:RT_EOK删除成功 ####1.3.5信号量获取 前者:如果信号量的计数器大于0,则将计数器减1,并立即返回成功。如果信号量的计数器等于0,则当前线程将被阻塞,直到计数器大于0或超时。 后者:如果信号量的计数器等于0,则立即返回失败,不会阻塞当前线程。(等价与rt_sem_take(sem,RT_WATTING_NO) 参数: sem:要获取的信号量的句柄。 time:等待信号量的超时时间,单位为毫秒。如果设置为 RT_WAITING_FOREVER,则表示无限等待;如果设置为 RT_WAITING_NO,则表示不等待,立即返回。 ```c 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_err_t:若成功获取信号量则返回 RT_EOK。 ####1.3.6信号量释放 这个函数通常与rt_sem_take配对使用,用于释放通过rt_sem_take获取的信号量。释放信号量后,其他等待该信号量的线程将有机会获取到信号量并继续执行。 参数: sem:要释放的信号量的句柄。 ```c rt_err_t rt_sem_release(rt_sem_t sem) ``` ###1.4代码示例 实现功能:临界区变量flags在1,2之间跳转。 ```c /* * Copyright (c) 2023, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2023-07-06 Supperthomas first version * 2023-12-03 Meco Man support nano version */ #include
#include
#include
#ifndef RT_USING_NANO #include
#endif /* RT_USING_NANO */ #include
rt_sem_t sem1; struct rt_semaphore sem2; int flags = 1; rt_thread_t th1,th2; void th1_entry(void *paramters) { while (1) { rt_thread_mdelay(1000); rt_sem_take(sem1,RT_WAITING_FOREVER); /* code */ flags++; if(flags ==100) { flags = 0; } rt_kprintf("th1_entry%d\n",flags); rt_sem_release(&sem2); rt_thread_mdelay(1000); } } void th2_entry(void *paramters) { while (1) { rt_thread_mdelay(1000); rt_sem_take(&sem2,RT_WAITING_FOREVER); /* code */ if(flags>0) { flags --; } rt_kprintf("th2_entry%d\n",flags); rt_sem_release(sem1); rt_thread_mdelay(1000); } } int main(void) { int ret = 0; sem1 = rt_sem_create("sem1",1,RT_IPC_FLAG_FIFO); ret = rt_sem_init(&sem2,"sem2",0,RT_IPC_FLAG_FIFO); th1 = rt_thread_create("th1",th1_entry,RT_NULL,512,15,5); th2 = rt_thread_create("th2",th2_entry,RT_NULL,512,15,5); if (sem1 == RT_NULL) { rt_kprintf("sem1 create failed...\n"); } else { rt_kprintf("sem1 create succeed...\n"); } if (ret<0) { rt_kprintf("sem2 create failed...\n"); } else { rt_kprintf("sem2 createsucceed\n"); } if (th1 == RT_NULL) { rt_kprintf("th1 create failed...\n"); } else { rt_thread_startup(th1); rt_kprintf("th1 createsucceed\n"); } if (th2 == RT_NULL) { rt_kprintf("th2 create failed...\n"); } else { rt_thread_startup(th2); rt_kprintf("th2 createsucceed\n"); } } ``` ###1.5效果 ![screenshot_ba0663bc901101a1cb7d7010b88f134.png](https://oss-club.rt-thread.org/uploads/20240725/31502be95145fc618ea4b07a3e2b58ee.png) ##二、互斥量/互斥锁 ###2.1互斥量的概念 ###2.2工作机制 ###2.3互斥量的使用 创建/初始化,获取(上锁),释放(解锁),删除/脱离 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20240725/a4b7c6e348962299073f19f21a6f13d6.png.webp) ####2.3.1动态创建互斥量与删除 参数: name:信号量名字 flag: define RT_IPC_FLAG_FIFO 0x00 //按照线程先进先出获取信号量资源 define RT_IPC_FLAG_PRIO 0x01 //按照线程优先级获取信号量资源 ```c rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag) rt_err_t rt_mutex_delete(rt_mutex_t mutex) ``` 不再使用互斥量时,通过删除互斥量以释放系统资源,适用于动态创建的互斥量 当删除一个互斥量时,所有等待此互斥量的线程都将被唤醒,等待线程获得的返回值是- RT_ERROR ####2.3.2静态创建互斥量与脱离 参数: mutex:结构体变量的地址 name:名字 flag: define RT_IPC_FLAG_FIFO 0x00 //按照线程先进先出获取信号量资源 define RT_IPC_FLAG_PRIO 0x01 //按照线程优先级获取信号量资源 ```c rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag) rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag) ``` 使用该函数接口后,内核先唤醒所有挂在该互斥量上的线程(线程的返回值是-RT_ERROR),然后系统将该互斥量从内核对象管理器中脱离。 ####2.3.3获取互斥量 ```c rt_err_t rt_mutex_take(rt_mutex_tmutex, rt_int32_t time) ``` ####2.3.4释放互斥量 ```c rt_err_t rt_mutex_release(rt_mutex_tmutex) ``` ###2.4代码示例 ```c /* * Copyright (c) 2023, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2023-07-06 Supperthomas first version * 2023-12-03 Meco Man support nano version */ #include
#include
#include
#ifndef RT_USING_NANO #include
#endif /* RT_USING_NANO */ #include
int flag1 = 0,flag2 = 0; rt_mutex_t mutex1; struct rt_mutex mutex2; rt_thread_t th1,th2; void th1_entry(void *p) { while (1) { rt_mutex_take(mutex1,RT_WAITING_FOREVER); flag1++; /* code */ rt_thread_mdelay(1000); flag2++; rt_mutex_release(mutex1); } } void th2_entry(void *p) { while (1) { rt_mutex_take(mutex1,RT_WAITING_FOREVER); flag1++; flag2++; rt_mutex_release(mutex1); /* code */ rt_kprintf("flag1:%d,flag2:%d\n",flag1,flag2); rt_thread_mdelay(1000); } } int main(void) { int ret = 0; mutex1 = rt_mutex_create("mutex1",RT_IPC_FLAG_FIFO); ret = rt_mutex_init(&mutex2,"mutex2",RT_IPC_FLAG_FIFO); th1 = rt_thread_create("th1",th1_entry,RT_NULL,512,20,5); th2 = rt_thread_create("th2",th2_entry,RT_NULL,512,20,5); if(mutex1 == RT_NULL) { rt_kprintf("mutex1 failed...\n"); } else rt_kprintf("mutex1 succeed...\n"); if(ret < 0) { rt_kprintf("mutex2 failed...\n"); } else rt_kprintf("mutex2 succeed...\n"); if(th1 == RT_NULL) { rt_kprintf("th1 failed...\n"); } else rt_kprintf("th1 succeed...\n");rt_thread_startup(th1); if(th2 == RT_NULL) { rt_kprintf("th2 failed...\n"); } else rt_kprintf("th2 succeed...\n");rt_thread_startup(th2); return RT_EOK; } ``` ###2.5效果 ![screenshot_0bb92a4d0d31f38d7c19f6e89295ee9.png](https://oss-club.rt-thread.org/uploads/20240725/13f0eb00fd0a125eaadc1b6975beb76e.png) ##三、事件集 ###3.1工作机制 1)事件只与线程相关,事件间相互独立:每个线程可拥有 32 个事件标志,采用一个 32 bit 无符号整型数进行记录,每一个 bit 代表一个事件; 2)事件仅用于同步,不提供数据传输功能; 3)事件无排队性,即多次向线程发送同一事件 (如果线程还未来得及读走),其效果等同于只发送一次。 ###3.2事件集的使用 创建 / 初始化事件集、发送事件、接收事件、删除 / 脱离事件集。 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20240725/62f04a2195c397d01ffeac7ece67b10c.png) ###3.3代码例程 ```c /* * Copyright (c) 2023, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2023-07-06 Supperthomas first version * 2023-12-03 Meco Man support nano version */ #include
#include
#include
#ifndef RT_USING_NANO #include
#endif /* RT_USING_NANO */ #include
rt_event_t event1; rt_thread_t th1,th2,th3; #define EVENT_FLAGS_1 (0x01<<0) #define EVENT_FLAGS_2 (0x01<<1) #define EVENT_FLAGS_3 (0x01<<2) void th1_entry(void *parameter) { while(1){ rt_event_recv(event1,EVENT_FLAGS_1,RT_EVENT_FLAG_CLEAR | RT_EVENT_FLAG_AND, RT_WAITING_FOREVER,NULL); rt_kprintf("th1_entry...\n"); rt_event_send(event1,EVENT_FLAGS_2); rt_thread_mdelay(1000); } } void th2_entry(void *parameter) { while(1){ rt_event_recv(event1,EVENT_FLAGS_2,RT_EVENT_FLAG_CLEAR | RT_EVENT_FLAG_AND,\ RT_WAITING_FOREVER,NULL); rt_kprintf("th2_entry...\n"); rt_event_send(event1,EVENT_FLAGS_3); rt_thread_mdelay(1000); } } void th3_entry(void *parameter) { while(1){ rt_event_recv(event1,EVENT_FLAGS_3,RT_EVENT_FLAG_CLEAR | RT_EVENT_FLAG_AND,\ RT_WAITING_FOREVER,NULL); rt_kprintf("th3_entry...\n"); rt_event_send(event1,EVENT_FLAGS_1); rt_thread_mdelay(1000); } } int main(void) { event1 = rt_event_create("set1", RT_IPC_FLAG_FIFO); if(event1 == RT_NULL) { LOG_E("rt_event_create failed...\n"); return -ENOMEM; } LOG_D("rt_event_create succeed...\n"); th1 = rt_thread_create("th1",th1_entry,RT_NULL,512,10,5); if(th1 == RT_NULL){ LOG_E("th1 rt_thread_create failed...\n"); return -ENOMEM; } th2 = rt_thread_create("th2",th2_entry,RT_NULL,512,10,5); if(th2 == RT_NULL){ LOG_E("th2 rt_thread_create failed...\n"); return -ENOMEM; } th3 = rt_thread_create("th3",th3_entry,RT_NULL,512,10,5); if(th3 == RT_NULL){ LOG_E("th3 rt_thread_create failed...\n"); return -ENOMEM; } rt_thread_startup(th1); rt_thread_startup(th3); rt_thread_startup(th2); rt_event_send(event1,EVENT_FLAGS_1); return RT_EOK; } ``` ###3.4结果 ![screenshot_27eeff5872494a886f4c3e1c45ca65a.png](https://oss-club.rt-thread.org/uploads/20240725/fc960bb8c186990804414ad19942a210.png)
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
24届暑期夏令营王伽利
这家伙很懒,什么也没写!
文章
3
回答
0
被采纳
0
关注TA
发私信
相关文章
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
cubemx
PWM
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
SFUD
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
出出啊
1518
个答案
343
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
813
个答案
177
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
149
次被采纳
本月文章贡献
出出啊
1
篇文章
1
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
3
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部