1、互斥量概念
互斥量是一种特殊的二值信号量,只有两种状态:开锁或闭锁;可用于对临界资源的保护从而实现独占式访问。
当一个线程A持有互斥量时,其他线程不能进行开锁或持有,但线程A能够再次获得这个锁而不被挂起。
这个特性与一般的二值信号量有很大的不同:在信号量中,线程递归持有会发生主动挂起,最终形成死锁。
信号量为1,take take release release,第一个take后信号量为0,第二个take就会挂起等待,而使得该信号量一直没机会释放造成死锁。
2、互斥量的特性——优先级继承
2.1、优先级翻转问题
信号量被低优先级线程C持有,而线程C在运行中被中等优先级线程B抢占,造成高优先级线程A被较低优先级的线程阻塞。
2.2、解决:优先级继承
线程 A 尝试获取共享资源而被挂起的期间,将线程 C 的优先级提升到线程 A 的优先级,防止C被B抢占,而当低优先级线程C释放该资源时,优先级重新回到初始设定。
这是一个过河拆桥的悲伤故事:当C拥有资源M的时候,如果大佬A也想要M,大佬A就假意带C混,此时A、C的优先级相同,但当C不再拥有资源M后,对A没什么用,立刻就被踢走C就滚回原先的优先级了。
3、互斥量API
3.1、创建和删除互斥量
- 静态互斥量
rt_err_t rt_mutex_init (rt_mutex_t mutex, const char* name, rt_uint8_t flag);
参数 | 描述 |
---|---|
mutex | 互斥量对象的句柄,它由用户提供,并指向互斥量对象的内存块 |
name | 互斥量的名称 |
flag | 该标志已经作废,无论用户选择 RT_IPC_FLAG_PRIO 还是 RT_IPC_FLAG_FIFO,内核均按照 RT_IPC_FLAG_PRIO 处理 |
返回 | —— |
RT_EOK | 初始化成功 |
rt_err_t rt_mutex_detach (rt_mutex_t mutex);
参数 | 描述 |
---|---|
mutex | 互斥量对象的句柄 |
返回 | —— |
RT_EOK | 成功 |
- 动态互斥量
rt_mutex_t rt_mutex_create (const char* name, rt_uint8_t flag);
参数 | 描述 |
---|---|
name | 互斥量的名称 |
flag | 该标志已经作废,无论用户选择 RT_IPC_FLAG_PRIO 还是 RT_IPC_FLAG_FIFO,内核均按照 RT_IPC_FLAG_PRIO 处理 |
返回 | —— |
互斥量句柄 | 创建成功 |
RT_NULL | 创建失败 |
rt_err_t rt_mutex_delete (rt_mutex_t mutex);
参数 | 描述 |
---|---|
mutex | 互斥量对象的句柄 |
返回 | —— |
RT_EOK | 删除成功 |
3.2、获取互斥量
rt_err_t rt_mutex_take (rt_mutex_t mutex, rt_int32_t time);
参数 | 描述 |
---|---|
mutex | 互斥量对象的句柄 |
time | 指定等待的时间 |
返回 | —— |
RT_EOK | 成功获得互斥量 |
-RT_ETIMEOUT | 超时 |
-RT_ERROR | 获取失败 |
rt_err_t rt_mutex_trytake(rt_mutex_t mutex);
参数 | 描述 |
---|---|
mutex | 互斥量对象的句柄 |
返回 | —— |
RT_EOK | 成功获得互斥量 |
-RT_ETIMEOUT | 获取失败 |
3.3、释放互斥量
rt_err_t rt_mutex_release(rt_mutex_t mutex);
参数 | 描述 |
---|---|
mutex | 互斥量对象的句柄 |
返回 | —— |
RT_EOK | 成功 |
4、优先级继承的示例代码及说明
初始状态:线程1优先级9,线程2优先级10,线程3优先级11;
- 线程1先运行,线程1挂起100ms;
- 线程2运行,线程2挂起50ms;
- 线程3运行,线程3获取互斥量,并将持有500ms;
- 50ms到了,切换线程2运行,线程2试图获取互斥量,此时互斥量被低优先级的线程3拥有,提升线程3优先级为10,线程2挂起,线程3接着运行;
- 100ms到了,切换线程1运行,此时线程2和线程3的优先级都是10,线程1运行结束;
- 线程3运行直到释放互斥量,线程3优先级恢复为11;
- 切换高优先级线程2运行,获取互斥量,释放互斥量,线程2结束;
- 线程3运行,打印当前优先级,线程3结束;
在官方的例程上做了一些修改,对执行的过程转变的了解更细致一些,只截图最后线程3又恢复优先级和线程2的切换;
#include <rtthread.h>
/* 指向线程控制块的指针 */
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;
static rt_thread_t tid3 = RT_NULL;
static rt_mutex_t mutex = RT_NULL;
#define THREAD_PRIORITY 10
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 5
/* 线程 1 入口 */
static void thread1_entry(void *parameter)
{
rt_kprintf("the priority of thread1 is: %d\n", tid1->current_priority);
/* 先让低优先级线程运行 */
rt_thread_mdelay(100);
/* 此时 thread3 持有 mutex,并且 thread2 等待持有 mutex */
/* 检查 rt_kprintf("the producer generates a number: %d\n", array[set%MAXSEM]); 与 thread3 的优先级情况 */
if (tid2->current_priority != tid3->current_priority)
{
/* 优先级不相同,测试失败 */
rt_kprintf("the priority of thread2 is: %d\n", tid2->current_priority);
rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);
rt_kprintf("test failed.\n");
return;
}
else
{
rt_kprintf("the priority of thread2 is: %d\n", tid2->current_priority);
rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);
rt_kprintf("test OK.\n");
}
rt_kprintf("thread1 exit\n");
}
/* 线程 2 入口 */
static void thread2_entry(void *parameter)
{
rt_err_t result;
rt_kprintf("the priority of thread2 is: %d\n", tid2->current_priority);
/* 先让低优先级线程运行 */
rt_thread_mdelay(50);
rt_kprintf("thread2 try mutex\n");
/*
* 试图持有互斥锁,此时 thread3 持有,应把 thread3 的优先级提升
* 到 thread2 相同的优先级
*/
result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
if (result == RT_EOK)
{
rt_kprintf("thread2 try mutex ok\n");
/* 释放互斥锁 */
rt_mutex_release(mutex);
rt_kprintf("thread2 release mutex\n");
}
rt_kprintf("thread2 exit\n");
}
/* 线程 3 入口 */
static void thread3_entry(void *parameter)
{
rt_tick_t tick;
rt_err_t result;
rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);
result = rt_mutex_take(mutex, RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_kprintf("thread3 take a mutex, failed.\n");
}
/* 做一个长时间的循环,500ms */
tick = rt_tick_get();
while (rt_tick_get() - tick < (RT_TICK_PER_SECOND / 2)){
rt_kprintf("thread3 is waiting 500ms.\n");
}
rt_mutex_release(mutex);
rt_kprintf("the priority of thread3 is: %d\n", tid3->current_priority);
rt_kprintf("thread3 exit\n");
}
int pri_inversion(void)
{
/* 创建互斥锁 */
mutex = rt_mutex_create("mutex", RT_IPC_FLAG_FIFO);
if (mutex == RT_NULL){
rt_kprintf("create dynamic mutex failed.\n");
return -1;
}
/* 创建线程 */
tid1 = rt_thread_create("thread1",thread1_entry, RT_NULL,
THREAD_STACK_SIZE,THREAD_PRIORITY - 1, THREAD_TIMESLICE);
if (tid1 != RT_NULL) rt_thread_startup(tid1);
tid2 = rt_thread_create("thread2",thread2_entry, RT_NULL,
THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
if (tid2 != RT_NULL) rt_thread_startup(tid2);
tid3 = rt_thread_create("thread3",thread3_entry, RT_NULL,
THREAD_STACK_SIZE, THREAD_PRIORITY + 1, THREAD_TIMESLICE);
if (tid3 != RT_NULL) rt_thread_startup(tid3);
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(pri_inversion, pri_inversion sample);