RTT学习笔记4-线程同步
基本概念
- 同步是指按预定的先后次序进行运行
- 在访问临界区的时候只允许一个 (或一类) 线程运行
进入 / 退出临界区的方式
1)调用 rt_hw_interrupt_disable() 进入临界区,调用 rt_hw_interrupt_enable() 退出临界区;详见《中断管理》的全局中断开关内容。
2)调用 rt_enter_critical() 进入临界区,调用 rt_exit_critical() 退出临界区。
信号量
- 信号量是一种轻型的用于解决线程间同步问题的内核对象,线程可以获取或释放它,从而达到同步或互斥的目的
- 假如信号量值为 5,则表示共有 5 个信号量实例(资源)可以被使用,当信号量实例数目为零时,再申请该信号量的线程就会被挂起在该信号量的等待队列上,等待可用的信号量实例(资源)
控制块
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;
信号量函数
动态
创建
rt_sem_t rt_sem_create(const char *name,rt_uint32_t value,rt_uint8_t flag);
flag可选:
RT_IPC_FLAG_FIFO //非实时性RT_IPC_FLAG_PRIO,即确保线程的实时性
删除
rt_err_t rt_sem_delete(rt_sem_t sem);
静态
创建
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_detach(rt_sem_t sem);
获取信号量
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_sem_release(rt_sem_t sem);
信号量使用例子
#include <rtthread.h>#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++;}elsereturn;/* count 每计数 10 次,就释放一次信号量 */if(0 == (count % 10)){rt_kprintf("t1 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("t2 take a dynamic semaphore, failed.\n");rt_sem_delete(dynamic_sem);return;}else{number++;rt_kprintf("t2 take a dynamic semaphore. number = %d\n" ,number);}}
}/* 信号量示例的初始化 */
int semaphore_sample(void)
{/* 创建一个动态信号量,初始值是 0 */dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_PRIO);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);
信号量的使用场景
信号量是一种非常灵活的同步方式,可以运用在多种场合中。形成锁、同步、资源计数等关系,也能方便的用于线程与线程、中断与线程间的同步中。
互斥量
信号量的问题
- 使用信号量会导致的另一个潜在问题是线程优先级翻转问题
- 在信号量中,因为已经不存在实例,线程递归持有会发生主动挂起(最终形成死锁)
互斥量控制块
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;
互斥量相关函数
动态创建和释放互斥量
rt_mutex_t rt_mutex_create (const char* name, rt_uint8_t flag);rt_err_t rt_mutex_delete (rt_mutex_t mutex);
静态创建和释放互斥锁
rt_err_t rt_mutex_init (rt_mutex_t mutex, const char* name, rt_uint8_t flag);rt_err_t rt_mutex_detach (rt_mutex_t mutex);
获取互斥锁
rt_err_t rt_mutex_take (rt_mutex_t mutex, rt_int32_t time);
窥探互斥锁
rt_err_t rt_mutex_trytake(rt_mutex_t mutex);
释放互斥量
rt_err_t rt_mutex_release(rt_mutex_t mutex);
互斥量使用示例
#include <rtthread.h>#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_PRIO);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);
事件集
事件集也是线程间同步的机制之一,一个事件集可以包含多个事件,利用事件集可以完成一对多,多对多的线程间同步
可以实现满足多个条件时候才实现同步
这种多个事件的集合可以用一个 32 位无符号整型变量来表示,变量的每一位代表一个事件,线程通过 逻辑与 或逻辑或将一个或多个事件关联起来,形成事件组合。事件的 逻辑或也称为是独立型同步,指的是线程与任何事件之一发生同步;事件逻辑与 也称为是关联型同步,指的是线程与若干事件都发生同步。
事件集有以下特点:
- 事件只与线程相关,事件间相互独立:每个线程可拥有 32 个事件标志,采用一个 32 bit 无符号整型数进行记录,每一个 bit 代表一个事件;
- 事件仅用于同步,不提供数据传输功能
- 事件无排队性,即多次向线程发送同一事件 (如果线程还未来得及读走),其效果等同于只发送一次。
事件信息标记
每个线程都拥有一个事件信息标记,它有三个属性,分别是
- RT_EVENT_FLAG_AND(逻辑与),
- RT_EVENT_FLAG_OR(逻辑或)
- RT_EVENT_FLAG_CLEAR(清除标记)
事件集控制块
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;
事件常用函数
动态创建事件集
rt_event_t rt_event_create(const char* name, rt_uint8_t flag);
rt_err_t rt_event_delete(rt_event_t event);
静态创建事件集
rt_err_t rt_event_init(rt_event_t event, const char* name, rt_uint8_t flag);
rt_err_t rt_event_detach(rt_event_t event);
发送事件
可以一次发送多个事件
rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set);
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);
option 的值可取:
/* 选择 逻辑与 或 逻辑或 的方式接收事件 */
RT_EVENT_FLAG_OR
RT_EVENT_FLAG_AND/* 选择清除重置事件标志位 */
RT_EVENT_FLAG_CLEAR
事件使用示例
#include <rtthread.h>#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_PRIO);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);
事件集的使用场合
- 事件集可使用于多种场合,它能够在一定程度上替代信号量,用于线程间同步。
- 一个线程或中断服务例程发送一个事件给事件集对象,而后等待的线程被唤醒并对相应的事件进行处理
- 与信号量不同的是,事件的发送操作在事件未清除前,是不可累计的,而信号量的释放动作是累计的
RTT学习笔记4-线程同步相关推荐
- C#学习笔记之线程 - 同步上下文
同步上下文(Synchronization Contexts) 手动使用锁的一个替代方案是去声明锁.通过派生ContextBoundObject和应用Synchronization属性,你告诉CLR自 ...
- 【Linux学习笔记】线程同步 之 信号量 之 sem_t结构体
sem_t结构体: 里面封装了两个数据: 1.__align:long int 型,保存了信号量的值: 2.__size:char型,功能未知,常为空值,一般不需要使用; sem_t mutex;se ...
- VS2017 CUDA编程学习8:线程同步-原子操作
文章目录 前言 1. 原子操作的理解 2. C++ CUDA实现原子操作 3. 执行结果 总结 学习资料 VS2017 CUDA编程学习1:CUDA编程两变量加法运算 VS2017 CUDA编程学习2 ...
- java学习笔记15--多线程编程基础2
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note15.html,转载请注明源地址. 线程的生命周期 1.线程的生命周期 线程从产生到消亡 ...
- 多人网络游戏服务器开发基础学习笔记 II: 帧同步 | 游戏客户端预测原理分析 | FPS 游戏状态同步
这篇是对书本 网络多人游戏架构与编程 的学习第二篇(第一篇:多人网络游戏服务器开发基础学习笔记 I:基本知识 | 游戏设计模式 | 网游服务器层次结构 | 游戏对象序列化 | 游戏 RPC 框架 | ...
- java学习笔记14--多线程编程基础1
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note14.html,转载请注明源地址. 多线程编程基础 多进程 一个独立程序的每一次运行称为 ...
- 关于java线程同步的笔记_线程同步(JAVA笔记-线程基础篇)
在多线程应用程序中经常会遇到线程同步的问题.比如:两个线程A.线程B可能会 "同时" 执行同一段代码,或修改同一个变量.而很多时候我们是不希望这样的. 这时候,就需要用到线程同步. ...
- 5G NR - RACH学习笔记2 - 上行同步/Timing Advance
UE在获取下行同步(PSS/SSS)后, 需要通过RACH过程(随机接入过程)接入到基站(gNodeB),这个时候基站由于尚未与UE达到上行同步,因此首先需要执行上行同步. 那么怎么达到上行同步? - ...
- java笔记--关于线程同步(7种同步方式)
关于线程同步(7种方式) --如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3897440.html"谢谢-- 为何要使用同步? ...
最新文章
- 查看mysql主外键信息
- android休眠唤醒驱动流程分析【转】
- Wireshark抓包—maybe caused by 'IP chechsum offload'?
- XML解析之JAXP案例详解
- java ajax 定时刷新_用ajax技术实现无闪烁定时刷新页面
- Spring注解——使用@ComponentScan自动扫描组件
- 深度残差收缩网络:(三)网络结构
- 模仿vue自己动手写响应式框架( - v-for
- 看看什么样的人适合网上开店( 转载)
- Python+OpenCV:图像修复(Image Inpainting)
- SQL Server:如何加入第一行
- 对警报线程池的警报线程_使用警报控制器的iOS操作表
- java 开源进销存项目_JSH_ERP 开源版J2EE进销存系统代码源码下载|JSH_ERP 开源版J2EE进销存系统代码源码官方下载-太平洋下载中心...
- Phyton学习笔记
- 连个字体反爬都搞不定?你还说你会爬虫?看完这篇就会了。
- VB程序设计教程(第四版)龚沛曾 实验8-2
- 知识型IP与网红的区别
- c# MouseClick和MouseDown的区别
- USRP工作流程及各部分功能
- 联通短消息服务器域名,中国联通域名服务器地址
热门文章
- Matlab优化求解器中的Tolerances and Stopping Criteria
- 商淘多b2b2c商城系统怎么在个人电脑上安装_企业怎么做好b2b2c商城网站建设?...
- 电力装置的继电保护和自动装置设计规范_继电保护装置升级到微机保护装置
- 拼接 结果集_JUST技术:利用轨迹拼接分析实时可达区域|技术前沿
- fastapi学习(二):路径参数获取与GET、POST参数获取
- 推荐系统学习(三)SVD奇异值分解做推荐与python代码
- python保存与加载LGBM模型,并解决报错TypeError: Need at least one training dataset or model file or model string..
- pandas时间序列与自回归ARIMA模型
- pythonfor循环加2_python中for循环如何实现每次控制变量翻倍
- HTTP1.1/2.0与QUIC协议