当前位置: 首页 > news >正文

FreeRTOS_互斥量_学习笔记

互斥量

数值只有0或1
谁获得互斥量,就必须由谁释放同一个互斥量。在这里插入图片描述
但其实在freeRTOS中,任务A获取的互斥锁,任务B也能释放。因此谁上锁谁开锁只是约定,在程序实现上不是强制的。
可重入的函数"是指:多个任务同时调用它、任务和中断同时调用它,函数的运行也是安全的。可重入的函数也被称为"线程安全”(thread safe)。
每个任务都维持自己的栈、自己的CPU寄存器,如果一个函数只使用局部变量,那么它就是线程安全的。
函数中一旦使用了全局变量、静态变量、其他外设,它就不是"可重入的",如果该函数正在被调用,就必须阻止其他任务、中断再次调用它。
上述问题的解决方法是:任务A访问这些全局变量、函数代码时,独占它,就是上个锁。这些全局变量、函数代码必须被独占地使用,它们被称为临界资源。
互斥量的使用过程如下:

互斥量初始值为1
任务A想访问临界资源,先获得并占有互斥量,然后开始访问
任务B也想访问临界资源,也要先获得互斥量:被别人占有了,于是阻塞
任务A使用完毕,释放互斥量;任务B被唤醒、得到并占有互斥量,然后开始访问临界资源
任务B使用完毕,释放互斥量

互斥量函数

使用互斥量需要定义:

#define configUSE_MUTEXES 1

创建

成功则返回句柄,失败则返回NULL

SemaphoreHandle_t xSemaphoreCreateMutex( void ); // 动态分配
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer );

获取与释放

不能在中断中使用。除此之外与信号量相同

void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );//删除
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );//释放
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);//获取

优先级反转问题

使用信号量时,有时会出现低优先级先运行,高优先级不运行的问题,原因在于:
低优先级的任务先创建,先获取了信号量;
不需要取信号量的中优先级任务第二个创建,阻塞了低优先级任务,此时低优先级任务信号量还没释放;
高优先级任务后创建,此时take信号量,被阻塞,无法运行
在FreeRTOS_信号量_学习笔记中,修改三个任务的优先级:

    xTaskCreate(CarTask, "car1", 128, &g_cars[0], osPriorityNormal, NULL);xTaskCreate(Car2Task, "car2", 128, &g_cars[1], osPriorityNormal+1, NULL);xTaskCreate(Car3Task, "car3", 128, &g_cars[2], osPriorityNormal+2, NULL);	

同时将第二辆车的任务中获取信号量和释放信号量的语句删除。此时CarTask被Car2Task阻塞,Car3Task无法运行
此时,只要将信号量替换为互斥量即可解决优先级反转问题。其原理是用了优先级继承功能。
假设持有互斥锁的是任务A,如果更高优先级的任务B也尝试获得这个锁
任务B说:你既然持有宝剑,又不给我,那就继承我的愿望吧
于是任务A就继承了任务B的优先级
这就叫:优先级继承
等任务A释放互斥锁时,它就恢复为原来的优先级
互斥锁内部就实现了优先级的提升、恢复

递归锁

互锁

A获得互斥量M1
B获得互斥量M2
A还想获得M2才能运行,A阻塞
B还想获得M1才能运行,B阻塞
AB都阻塞,都无法释放其所持有的互斥量

自锁

A获得了互斥量M
他调用了一个库函数,库函数也想获取互斥量M
库函数阻塞,A休眠,无法释放互斥量

解决以上问题,可以使用递归锁,其特性为:

  1. 任务A获得递归锁M后,它还可以多次去获得这个锁
  2. "take"了N次,要"give"N次,这个锁才会被释放

递归锁的函数和一般互斥量的函数名不一样,参数类型一样。

递归锁互斥量
创建xSemaphoreCreateRecursiveMutexxSemaphoreCreateMutex
获得xSemaphoreTakeRecursivexSemaphoreTake
释放xSemaphoreGiveRecursivexSemaphoreGive

递归锁主要用于解决同一个线程多次获取同一个锁时的同步问题,不涉及多个线程之间的并发访问。

相关文章:

  • php 连接sqlserver步骤
  • 无网环境禁止 WPS 提示登录,且基本功能按钮可用
  • HQL面试题练习 —— 品牌营销活动天数
  • 运维的发展前景,会被AI取代吗?
  • python的requests爬虫模块使用代理ip方法---集合
  • python 庆余年2收视率数据分析与可视化
  • VUE3+TS+elementplus创建table,纯前端的table
  • Leaflet【二】图层绘制——UI图层【点线面】 矢量图层【img、svg】
  • Java NIO 基础
  • 使用DockerFile 编写 指令来构建镜像
  • continue插件二次开发调试并打包
  • notepad++ 批量转所有文件编码格式为UTF-8
  • c++中的constexpr 与decltype
  • 5.23 学习总结
  • Python KMP算法
  • [iOS]Core Data浅析一 -- 启用Core Data
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • 345-反转字符串中的元音字母
  • 5、React组件事件详解
  • Akka系列(七):Actor持久化之Akka persistence
  • Android路由框架AnnoRouter:使用Java接口来定义路由跳转
  • Angular js 常用指令ng-if、ng-class、ng-option、ng-value、ng-click是如何使用的?
  • CSS盒模型深入
  • C语言笔记(第一章:C语言编程)
  • iOS筛选菜单、分段选择器、导航栏、悬浮窗、转场动画、启动视频等源码
  • IP路由与转发
  • JavaScript函数式编程(一)
  • Java基本数据类型之Number
  • leetcode386. Lexicographical Numbers
  • oldjun 检测网站的经验
  • QQ浏览器x5内核的兼容性问题
  • 后端_ThinkPHP5
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 浏览器缓存机制分析
  • 使用common-codec进行md5加密
  • 数组的操作
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 在electron中实现跨域请求,无需更改服务器端设置
  • 转载:[译] 内容加速黑科技趣谈
  • 自动记录MySQL慢查询快照脚本
  • # SpringBoot 如何让指定的Bean先加载
  • #pragam once 和 #ifndef 预编译头
  • (3)选择元素——(14)接触DOM元素(Accessing DOM elements)
  • (day 12)JavaScript学习笔记(数组3)
  • (Matalb时序预测)PSO-BP粒子群算法优化BP神经网络的多维时序回归预测
  • (Matlab)使用竞争神经网络实现数据聚类
  • (附表设计)不是我吹!超级全面的权限系统设计方案面世了
  • (六)激光线扫描-三维重建
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (转)socket Aio demo
  • (转)Sql Server 保留几位小数的两种做法
  • (转)用.Net的File控件上传文件的解决方案
  • (转载)(官方)UE4--图像编程----着色器开发
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • .bat批处理(十):从路径字符串中截取盘符、文件名、后缀名等信息