Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
内核学习营
个人分享心得(五)——线程间同步(互斥量)
发布于 2019-03-28 16:00:34 浏览:1886
订阅该版
[tocm] * 本帖最后由 家定不举棋 于 2019-4-11 00:12 编辑 * # 互斥量 互斥量 `mutex` :一种相互排斥的信号量,他跟信号量的本质区别是互斥量中只有两种状态:开锁和闭锁。当有线程占用它的时候是闭锁状态,没有线程占用是开锁状态。 互斥量能防止线程优先级翻转,当有高优先级线程打断持有互斥量的线程时,线程被打断后该线程的优先级暂时提升为高优先级线程的优先级。 但是当互斥量被低优先级线程占有时,高优先级线程做出获取互斥量操作时会因为无法获取而被占用。 互斥量结构体: ``` struct rt_mutex { struct rt_ipc_object parent; rt_uint16_t value; rt_uint8_t original_priority; rt_uint8_t hold; struct rt_thread *owner; }; ``` 由结构体中可以看出其本质还是一个IPC内核对象 `value` 是互斥量的值,`original_priority` 是持有线程的原始优先级,`hold` 是线程的持有次数,`*owner` 是当前拥有互斥量的线程。 静态初始化 `rt_mutex_static` 或者动态创建 `rt_mutex_create` ,就是对其互斥量的对象进行初始化,然后将value的值赋值为1。 ##### !!!然后个人把 `value` 的值在外部设置成其他值,那么程序还能运行,并能够实现多个线程持有互斥量,这样验证了互斥量的本质就是信号量。 > 猜测:如果改变 `value` 的值后,由于互斥量拥有防止优先级翻转功能,然而信号量不能防止优先级翻转功能,那么是不是可以通过改变 `value` 使得互斥量变成一种“高级”的信号量。 用代码实验了一下,先将 `mutex->value` 赋值为2,建立两个优先级不同的线程,先启动低优先级的线程,然后启动高优先级的线程,然后分别获取互斥量,查看其输出情况: ``` #include
static rt_thread_t thread1, thread2; static rt_mutex_t mutex; static void thread1_entry(void *paramter) { rt_mutex_take(mutex, RT_WAITING_FOREVER); rt_kprintf("run thread1.
"); for (int count = 0; count < 10; count++) { rt_kprintf("thread1 count = %d.
", count); } rt_kprintf("thread1->current_priority = %d.
",thread1->current_priority); rt_kprintf("thread1:mutex->hold = %d, mutex->original_priority = %d, mutex->value = %d.
",mutex->hold, mutex->original_priority, mutex->value); } static void thread2_entry(void *paramter) { rt_mutex_take(mutex, RT_WAITING_FOREVER); rt_kprintf("run thread2.
"); for (int count = 0; count < 10; count++) { rt_kprintf("thread2 count = %d.
", count); } rt_kprintf("thread2->current_priority = %d.
",thread2->current_priority); rt_kprintf("thread2:mutex->hold = %d, mutex->original_priority = %d, mutex->value = %d.
",mutex->hold, mutex->original_priority, mutex->value); } int test_sample(void) { mutex = rt_mutex_create("mutex", RT_IPC_FLAG_PRIO); if (mutex == RT_NULL) { rt_kprintf("create mutex failed.
"); return -1; } mutex->value = 2; thread1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 1024, 16, 10); thread2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 1024, 15, 10); if (thread1 != RT_NULL && thread2 != RT_NULL) { rt_thread_startup(thread1); rt_thread_startup(thread2); } return 0; } MSH_CMD_EXPORT(test_sample, test sample); ``` 输出结果为: ``` \ | / - RT - Thread Operating System / | \ 3.1.0 build Feb 20 2019 2006 - 2018 Copyright by rt-thread team msh >te test_sample msh >test_sample run thread1. thread1 count = 0. thread1 count = 1. thread1 count = 2. thread1 count = 3. thread1 count = 4. thread1 count = 5. thread1 count = 6. thread1 count = 7. thread1 count = 8. thread1 count = 9. thread1->current_priority = 16. thread1:mutex->hold = 1, mutex->original_priority = 16, mutex->value = 1. run thread2. thread2 count = 0. thread2 count = 1. thread2 count = 2. thread2 count = 3. thread2 count = 4. thread2 count = 5. thread2 count = 6. thread2 count = 7. thread2 count = 8. thread2 count = 9. thread2->current_priority = 15. thread2:mutex->hold = 2, mutex->original_priority = 15, mutex->value = 0. msh > ``` 由上述可知在 `value` 值为2的情况下先获得互斥量的先运行并占用资源,若其不挂起则后获得互斥量的线程将一直等待,即使优先级较高。但是在不释放互斥量的前提下线程2依旧可以在线程1结束后获得互斥量而运行。 在判断先进先出原则 `RT_IPC_FLAG_FIFO` 和优先级优先原则 `RT_IPC_FLAG_PRIO` 时,产生了一个 bug 点:创建了两个优先级不同的线程,两个线程的入口函数均为获取互斥量 `rt_mutex_take(&test_mutex, RT_WAITING_FOREVER);`,打印 `rt_kprintf`,释放互斥量 `rt_mutex_release(&test_mutex);`,然后发现获取信号量的顺序与启动线程的顺序一致。 bug 原因:在当第一个线程启动后,线程立刻执行,便会立刻进行占用互斥量操作,当后面线程启动后,线程只能等待前面的线程释放资源。这就导致了线程并未同时等待互斥量的操作。 bug 解决: 建立三个线程,由第一个线程先获取互斥量,等待线程全部启动,然后第一个线程释放互斥量,让后面两个线程进行同时等待互斥量的操作,这样可清晰的看到互斥量标志的作用。 `thread_entry1` 优先级为15,`thread_entry2` 优先级为14,`thread_entry3` 优先级为13,启动顺序为 `thread1` -> `thread2` -> `thread3`。 部分代码如下: ``` rt_mutex_init(&test_mutex, "test_mutex", RT_IPC_FLAG_PRIO); rt_thread_startup(thread1); rt_thread_startup(thread2); rt_thread_startup(thread3); static void thread_entry1(void *arg) { rt_mutex_take(&test_mutex, RT_WAITING_FOREVER); rt_thread_mdelay(10); rt_kprintf("run thread1.
"); rt_mutex_release(&test_mutex); } static void thread_entry2(void *arg) { rt_mutex_take(&test_mutex, RT_WAITING_FOREVER); rt_kprintf("run thread2.
"); rt_mutex_release(&test_mutex); } static void thread_entry3(void *arg) { rt_mutex_take(&test_mutex, RT_WAITING_FOREVER); rt_kprintf("run thread3.
"); rt_mutex_release(&test_mutex); } ``` 输出打印结果为: ``` \ | / - RT - Thread Operating System / | \ 3.1.0 build Feb 20 2019 2006 - 2018 Copyright by rt-thread team msh >priority_inversion_start run thread1. run thread3. run thread2. ``` ##### !!!对于互斥量线程优先级翻转问题,有一个至关重要的特性:拥有互斥量资源的低优先级线程如果被高优先级线程因占用资源而打断,那么低优先级的线程当前优先级会翻转到高优先级的线程。 ##### !!!但是高优先级的线程如果不做占用资源的操作,那么低优先级的线程不会进行翻转操作 正常翻转的部分代码如下(线程依旧是上述线程,改变了线程入口函数): ``` static void prio_inv_entry1(void *arg) { rt_mutex_take(&test_mutex, RT_WAITING_FOREVER); rt_kprintf("hthread1->current_priority = %d.
",hthread1->current_priority); rt_thread_mdelay(10); rt_kprintf("run thread1.
"); rt_kprintf("hthread1->current_priority = %d.
",hthread1->current_priority); rt_mutex_release(&test_mutex); } static void prio_inv_entry2(void *arg) { rt_thread_mdelay(5); rt_kprintf("run thread2.
"); } static void prio_inv_entry3(void *arg) { rt_mutex_take(&test_mutex, RT_WAITING_FOREVER); rt_kprintf("run thread3.
"); rt_mutex_release(&test_mutex); } ``` 输出打印结果如下: ``` \ | / - RT - Thread Operating System / | \ 3.1.0 build Feb 20 2019 2006 - 2018 Copyright by rt-thread team msh >priority_inversion_start hthread1->current_priority = 15. run thread1. hthread1->current_priority = 13. run thread3. run thread2. ``` 但是如果 `thread3` 不做获取互斥量操作,优先级不会做出翻转,那么输出打印结果如下: ``` \ | / - RT - Thread Operating System / | \ 3.1.0 build Feb 20 2019 2006 - 2018 Copyright by rt-thread team msh >priority_inversion_start hthread1->current_priority = 15. run thread3. run thread2. run thread1. hthread1->current_priority = 15. ``` PS:有不足的或者错误的欢迎大家指点批评呀
查看更多
3
个回答
默认排序
按发布时间排序
Roomen
2019-03-29
这家伙很懒,什么也没写!
1楼小板凳做好了
andychen
2019-03-29
这家伙很懒,什么也没写!
写的不错
家定不举棋
2019-03-29
这家伙很懒,什么也没写!
>写的不错 --- 这一个月的学习总结吧,后面的路还长着呢,我会定期发论坛分享心得的,希望得到批评指点:lol:lol
撰写答案
登录
注册新账号
关注者
0
被浏览
1.9k
关于作者
家定不举棋
这家伙很懒,什么也没写!
提问
10
回答
6
被采纳
0
关注TA
发私信
相关问题
1
【内核学习】rtthread内核移植记录-STM32F103ZET6-HAL库
2
《内核学习营》+水一方+自用STM32F103VC 板RT-Thread内核移植分享
3
《内核学习营》+水一方+项目中创建标准的 RT-Thread工程
4
内核学习营+坦然+探索者stm32f407板子RT-thread循环点亮led灯
5
<内核学习营>+坦然+探索者stm32f407板子RT-thread串口字符点灯
6
<内核学习营>+坦然+探索者stm32f407板子RT-thread的pwm点灯实验
7
<内核学习营>+坦然+探索者stm32f407板子RT-thread串口实验
8
<内核学习营>+坦然+野火stm32f103板子RT-thread读写SD卡实验
9
<内核学习营>+坦然+探索者stm32f407板子RT-thread的RTC闹钟实验
10
【内核学习营】+王秀峰+led_rgb
推荐文章
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组件
最新文章
1
使用RC522软件包驱动FM1722
2
常量数据类型和表达式陷阱分享
3
进行i2c驱动移植的经验总结
4
在VSCode中使用clang-format
5
我该如何使用这个微雪的WIFI400 WIFI-LPB-100在rtt里或者我该怎样为它开发驱动
热门标签
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
UART
WIZnet_W5500
ota在线升级
freemodbus
PWM
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
编译报错
Debug
rt_mq_消息队列_msg_queue
SFUD
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
xiaorui
1
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部