Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
学习笔记
【rtthread学习笔记系列】第四篇:线程间同步
发布于 2021-05-12 11:19:06 浏览:1130
订阅该版
[tocm] # 一、线程间同步的概念 rtthread通过线程间同步建立线程间的执行顺序,多个线程访问的同一个内存叫做临界区。rtthread提供的同步的工具 * 信号量 * 互斥量 * 事件集 # 二、信号量 ## 2.1 信号量概念 rtthread将信号量抽象成rt_semaphore. ``` struct rt_semaphore { struct rt_ipc_object parent; /* 继 承 自 ipc_object 类 */ rt_uint16_t value; /* 信 号 量 的 值 */ }; /* rt_sem_t 是 指 向 semaphore 结 构 体 的 指 针 类 型 */ typedef struct rt_semaphore* rt_sem_t; ``` ## 2.2 信号量api ``` //创建信号量 /* name:信号量名称 value:信号量初始值 flag:标志,可取RT_IPC_FLAG_FIFO 或RT_IPC_FLAG_PRIO */ rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag); //删除信号量 /* sem:信号量句柄 */ rt_err_t rt_sem_delete(rt_sem_t sem); //初始化信号量 /* sem:信号量句柄 name:信号量名称 value:信号量初始值 flag:标志,可取RT_IPC_FLAG_FIFO 或 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) //信号量脱离 /* sem:信号量句柄 */ rt_err_t rt_sem_detach(rt_sem_t sem); //取得信号量 /* sem:信号量句柄 time:等待超时时间 */ rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time); //无等待获取信号量 /* sem:信号量句柄 */ rt_err_t rt_sem_trytake(rt_sem_t sem); //释放信号量 /* sem:信号量句柄 */ rt_err_t rt_sem_release(rt_sem_t sem); ``` ## 2.3 信号量示例 本示例定义了两个线程,线程1释放信号量,线程2取得信号量,信号量的初始值代表资源的数目。 ``` /* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2018-08-24 yangjie the first version */ /* * 程序清单:信号量例程 * * 该例程创建了一个动态信号量,初始化两个线程,线程1在count每计数10次时, * 发送一个信号量,线程2在接收信号量后,对number进行加1操作 */ #include
#define THREAD_PRIORITY 25 #define THREAD_TIMESLICE 5 /* 指向信号量的指针 */ static rt_sem_t dynamic_sem = RT_NULL; ALIGN(RT_ALIGN_SIZE) static char thread1_stack[1024]; static struct rt_thread thread1; static void rt_thread1_entry(void *parameter) { static rt_uint8_t count = 0; while (1) { if (count <= 100) { count++; } else return; /* count每计数10次,就释放一次信号量 */ if (0 == (count % 10)) { rt_kprintf("thread1 release a dynamic semaphore.\n"); rt_sem_release(dynamic_sem); } } } ALIGN(RT_ALIGN_SIZE) static char thread2_stack[1024]; static struct rt_thread thread2; static void rt_thread2_entry(void *parameter) { static rt_err_t result; static rt_uint8_t number = 0; while (1) { /* 永久方式等待信号量,获取到信号量,则执行number自加的操作 */ result = rt_sem_take(dynamic_sem, RT_WAITING_FOREVER); if (result != RT_EOK) { rt_kprintf("thread2 take a dynamic semaphore, failed.\n"); rt_sem_delete(dynamic_sem); return; } else { number++; rt_kprintf("thread2 take a dynamic semaphore. number = %d\n", number); } } } /* 信号量示例的初始化 */ int semaphore_sample() { /* 创建一个动态信号量,初始值是0 */ dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_FIFO); if (dynamic_sem == RT_NULL) { rt_kprintf("create dynamic semaphore failed.\n"); return -1; } else { rt_kprintf("create done. dynamic semaphore value = 0.\n"); } rt_thread_init(&thread1, "thread1", rt_thread1_entry, RT_NULL, &thread1_stack[0], sizeof(thread1_stack), THREAD_PRIORITY, THREAD_TIMESLICE); rt_thread_startup(&thread1); rt_thread_init(&thread2, "thread2", rt_thread2_entry, RT_NULL, &thread2_stack[0], sizeof(thread2_stack), THREAD_PRIORITY - 1, THREAD_TIMESLICE); rt_thread_startup(&thread2); return 0; } /* 导出到 msh 命令列表中 */ MSH_CMD_EXPORT(semaphore_sample, semaphore sample); ``` # 三、互斥量 ## 3.1 互斥量概念 rtthread将互斥量抽象成rt_mutex. ``` struct rt_mutex { struct rt_ipc_object parent; /* 继 承 自 ipc_object 类 */ rt_uint16_t value; /* 互 斥 量 的 值 */ rt_uint8_t original_priority; /* 持 有 线 程 的 原 始 优 先 级 */ rt_uint8_t hold; /* 持 有 线 程 的 持 有 次 数 */ struct rt_thread *owner; /* 当 前 拥 有 互 斥 量 的 线 程 */ }; /* rt_mutext_t 为 指 向 互 斥 量 结 构 体 的 指 针 类 型 */ typedef struct rt_mutex* rt_mutex_t; ``` ## 3.2 互斥量api ``` //创建互斥量 /* name:互斥量名称 flag:标志,可取RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO */ rt_mutex_t rt_mutex_create (const char* name, rt_uint8_t flag); //删除互斥量 /* mutex:互斥量句柄 */ rt_err_t rt_mutex_delete (rt_mutex_t mutex); //初始化互斥量 /* mutex:互斥量句柄 name:互斥量名称 flag:标志,可取RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO */ rt_err_t rt_mutex_init (rt_mutex_t mutex, const char* name, rt_uint8_t flag); //互斥量脱离 /* mutex:互斥量句柄 */ rt_err_t rt_mutex_detach (rt_mutex_t mutex) //获取互斥量 /* mutex:互斥量句柄 time:等待超时时间 */ rt_err_t rt_mutex_take (rt_mutex_t mutex, rt_int32_t time); //释放信号量 /* mutex:互斥量句柄 */ rt_err_t rt_mutex_release(rt_mutex_t mutex); ``` ## 3.3 互斥量示例 ``` /* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2018-08-24 yangjie the first version */ /* * 程序清单:互斥锁例程 * * 互斥锁是一种保护共享资源的方法。当一个线程拥有互斥锁的时候, * 可以保护共享资源不被其他线程破坏。线程1对2个number分别进行加1操作 * 线程2也会对2个number分别进行加1操作。使用互斥量保证2个number值保持一致 */ #include
#define THREAD_PRIORITY 8 #define THREAD_TIMESLICE 5 /* 指向互斥量的指针 */ static rt_mutex_t dynamic_mutex = RT_NULL; static rt_uint8_t number1, number2 = 0; ALIGN(RT_ALIGN_SIZE) static char thread1_stack[1024]; static struct rt_thread thread1; static void rt_thread_entry1(void *parameter) { while (1) { /* 线程1获取到互斥量后,先后对number1、number2进行加1操作,然后释放互斥量 */ rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER); number1++; rt_thread_mdelay(10); number2++; rt_mutex_release(dynamic_mutex); } } ALIGN(RT_ALIGN_SIZE) static char thread2_stack[1024]; static struct rt_thread thread2; static void rt_thread_entry2(void *parameter) { while (1) { /* 线程2获取到互斥量后,检查number1、number2的值是否相同,相同则表示mutex起到了锁的作用 */ rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER); if (number1 != number2) { rt_kprintf("not protect.number1 = %d, mumber2 = %d \n", number1, number2); } else { rt_kprintf("mutex protect ,number1 = mumber2 is %d\n", number1); } number1++; number2++; rt_mutex_release(dynamic_mutex); if (number1 >= 50) return; } } /* 互斥量示例的初始化 */ int mutex_sample(void) { /* 创建一个动态互斥量 */ dynamic_mutex = rt_mutex_create("dmutex", RT_IPC_FLAG_FIFO); if (dynamic_mutex == RT_NULL) { rt_kprintf("create dynamic mutex failed.\n"); return -1; } rt_thread_init(&thread1, "thread1", rt_thread_entry1, RT_NULL, &thread1_stack[0], sizeof(thread1_stack), THREAD_PRIORITY, THREAD_TIMESLICE); rt_thread_startup(&thread1); rt_thread_init(&thread2, "thread2", rt_thread_entry2, RT_NULL, &thread2_stack[0], sizeof(thread2_stack), THREAD_PRIORITY - 1, THREAD_TIMESLICE); rt_thread_startup(&thread2); return 0; } /* 导出到 msh 命令列表中 */ MSH_CMD_EXPORT(mutex_sample, mutex sample); ``` # 四、事件集 ## 4.1 事件集概念 rtthread将事件集抽象成rt_event ``` struct rt_event { struct rt_ipc_object parent; /* 继 承 自 ipc_object 类 */ /* 事 件 集 合, 每 一 bit 表 示 1 个 事 件, bit 位 的 值 可 以 标 记 某 事 件 是 否 发 生 */ rt_uint32_t set; }; /* rt_event_t 是 指 向 事 件 结 构 体 的 指 针 类 型 */ typedef struct rt_event* rt_event_t; ``` ## 4.2 事件集api ``` //创建事件集 /* name:事件集名称 flag:标志,可取RT_IPC_FLAG_FIFO 或RT_IPC_FLAG_PRIO */ rt_event_t rt_event_create(const char* name, rt_uint8_t flag); //删除事件集 /* event:事件集句柄 */ rt_err_t rt_event_delete(rt_event_t event); //初始化事件集 /* event:事件集句柄 name:事件集名称 flag:标志,可取RT_IPC_FLAG_FIFO 或RT_IPC_FLAG_PRIO */ rt_err_t rt_event_init(rt_event_t event, const char* name, rt_uint8_t flag); //事件集脱离 /* event:事件集句柄 */ rt_err_t rt_event_detach(rt_event_t event); //发送事件集 /* event:事件集句柄 set:事件标志 */ rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set); //接收事件 /* event:事件集句柄 set:事件标志 option: RT_EVENT_FLAG_OR、RT_EVENT_FLAG_AND /* 选 择 清 除 重 置 事 件 标 志 位 */ RT_EVENT_FLAG_CLEAR timeout:等待超时时间 recved:指向接收到的事件 */ rt_err_t rt_event_recv(rt_event_t event, rt_uint32_t set, rt_uint8_t option, rt_int32_t timeout, rt_uint32_t* recved); ``` ## 4.3 事件集示例 ``` /* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2018-08-24 yangjie the first version */ /* * 程序清单:事件例程 * * 程序会初始化2个线程及初始化一个静态事件对象 * 一个线程等待于事件对象上,以接收事件; * 一个线程发送事件 (事件3/事件5) */ #include
#define THREAD_PRIORITY 9 #define THREAD_TIMESLICE 5 #define EVENT_FLAG3 (1 << 3) #define EVENT_FLAG5 (1 << 5) /* 事件控制块 */ static struct rt_event event; ALIGN(RT_ALIGN_SIZE) static char thread1_stack[1024]; static struct rt_thread thread1; /* 线程1入口函数 */ static void thread1_recv_event(void *param) { rt_uint32_t e; /* 第一次接收事件,事件3或事件5任意一个可以触发线程1,接收完后清除事件标志 */ if (rt_event_recv(&event, (EVENT_FLAG3 | EVENT_FLAG5), RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &e) == RT_EOK) { rt_kprintf("thread1: OR recv event 0x%x\n", e); } rt_kprintf("thread1: delay 1s to prepare the second event\n"); rt_thread_mdelay(1000); /* 第二次接收事件,事件3和事件5均发生时才可以触发线程1,接收完后清除事件标志 */ if (rt_event_recv(&event, (EVENT_FLAG3 | EVENT_FLAG5), RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &e) == RT_EOK) { rt_kprintf("thread1: AND recv event 0x%x\n", e); } rt_kprintf("thread1 leave.\n"); } ALIGN(RT_ALIGN_SIZE) static char thread2_stack[1024]; static struct rt_thread thread2; /* 线程2入口 */ static void thread2_send_event(void *param) { rt_kprintf("thread2: send event3\n"); rt_event_send(&event, EVENT_FLAG3); rt_thread_mdelay(200); rt_kprintf("thread2: send event5\n"); rt_event_send(&event, EVENT_FLAG5); rt_thread_mdelay(200); rt_kprintf("thread2: send event3\n"); rt_event_send(&event, EVENT_FLAG3); rt_kprintf("thread2 leave.\n"); } int event_sample(void) { rt_err_t result; /* 初始化事件对象 */ result = rt_event_init(&event, "event", RT_IPC_FLAG_FIFO); if (result != RT_EOK) { rt_kprintf("init event failed.\n"); return -1; } rt_thread_init(&thread1, "thread1", thread1_recv_event, RT_NULL, &thread1_stack[0], sizeof(thread1_stack), THREAD_PRIORITY - 1, THREAD_TIMESLICE); rt_thread_startup(&thread1); rt_thread_init(&thread2, "thread2", thread2_send_event, RT_NULL, &thread2_stack[0], sizeof(thread2_stack), THREAD_PRIORITY, THREAD_TIMESLICE); rt_thread_startup(&thread2); return 0; } /* 导出到 msh 命令列表中 */ MSH_CMD_EXPORT(event_sample, event sample); ```
1
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
happycode999
这家伙很懒,什么也没写!
文章
28
回答
6
被采纳
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
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
SFUD
msh
rt_mq_消息队列_msg_queue
keil_MDK
ulog
MicroPython
C++_cpp
本月问答贡献
出出啊
1517
个答案
342
次被采纳
小小李sunny
1443
个答案
289
次被采纳
张世争
805
个答案
174
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
148
次被采纳
本月文章贡献
出出啊
1
篇文章
4
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
1
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部