互斥量之:与信号量区别;死锁;优先级翻转;递归锁

news/2024/5/20 13:15:51 标签: 互斥量, 死锁, 优先级翻转, 递归锁

一、互斥量

(一)互斥量的定义

互斥量的状态只有两种,开锁或闭锁(两种状态值)。当有线程持有它后,互斥量将处于闭锁状态,由这个线程获得它的所有权。相反,当这个线程释放它时,将对互斥量进行开锁,失去它的所有权。

当一个线程持有互斥量时,其他线程将不能够对它进行开锁或持有它,持有该互斥量的线程也能够再次获得这个锁而不被挂起。

多线程环境下往往存在多个线程竞争同一临界资源的应用场景,互斥量可被用于对临界资源的保护从而实现独占式访问。

(二)互斥量的使用

1、互斥量控制块

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;

 

2、获取互斥量

rt_err_t rt_mutex_take (rt_mutex_t mutex, rt_int32_t time);

如果互斥量没有被其他线程控制,那么申请该互斥量的线程将成功获得该互斥量如果互斥量已经被当前线程控制,则该互斥量的持有计数加 1,当前线程也不会挂起等待。如果互斥量已经被其他线程占有,则当前线程在该互斥量上挂起等待,直到其他线程释放它或者等待时间超过指定的超时时间。

3、释放互斥量

rt_err_t rt_mutex_release(rt_mutex_t mutex);

当线程完成互斥资源的访问后,应尽快释放它占据的互斥量,使得其他线程能及时获取该互斥量

使用该函数接口时,只有已经拥有互斥量控制权的线程才能释放它,当持有该互斥量的线程释放该互斥量,该互斥量的持有计数就减 1。当该互斥量持有计数为零时互斥量变为可用(开锁状态),等待在该信号量上的线程将被唤醒。如果线程的运行优先级被互斥量提升,那么当互斥量被释放后,线程恢复为持有互斥量前的优先级。

 

(三)互斥量的使用场合

互斥量的使用比较单一,因为它是信号量的一种,并且它是以锁的形式存在。在初始化的时候,互斥量永远都处于开锁的状态,而被线程持有的时候则立刻转为闭锁的状态。互斥量更适合于:

(1)线程多次持有互斥量的情况下。这样可以避免同一线程多次递归持有而造成死锁的问题。

(2)可能会由于多线程同步而造成优先级翻转的情况。

注:需要切记的是互斥量不能在中断服务例程中使用。

(四)与信号量的区别

1)一个互斥量在同一时刻只能被一个线程持有,而不是像信号量可以同时被多个线程持有(信号量值≥2)。

2)互斥量支持递归访问,而信号量递归持有会发生主动挂起,最终形成死锁

3)互斥量不会造成线程优先级翻转,而信号量使用不当可能会造成优先级翻转

4)互斥量只能由持有线程释放,而信号量则可以由任何线程释放。

注:互斥量是0开锁,1闭锁;而信号量是0闭锁,≥1是释放(开锁)。

三、互斥量相关

(一)优先级翻转

优先级翻转是当一个高优先级任务通过信号量机制访问共享资源时,该信号量已被一低优先级任务占有,因此造成高优先级任务被许多具有较低优先级任务阻塞,实时性难以得到保证。

例如:有优先级为A、B和C三个任务,优先级A>B>C,任务A,B处于挂起状态,等待某一事件发生,任务C正在运行,此时任务C开始使用某一共享资源S。在使用中,任务A等待事件到来,任务A转为就绪态,因为它比任务C优先级高,所以立即执行。当任务A要使用共享资源S时,由于其正在被任务C使用,因此任务A被挂起,任务C开始运行。如果此时任务B等待事件到来,则任务B转为就绪态。由于任务B优先级比任务C高,因此任务B开始运行,直到其运行完毕,任务C才开始运行。直到任务C释放共享资源S后,任务A才得以执行。在这种情况下,优先级发生了翻转,任务B先于任务A运行。

(二)优先级继承算法

优先级继承算法可以很好的解决优先级翻转问题。

优先级继承是通过在线程 A 尝试获取共享资源而被挂起的期间内,将线程 C 的优先级提升到线程 A 的优先级别,从而解决优先级翻转引起的问题。这样能够防止 C(间接地防止 A)被 B 抢占,如下图所示。优先级继承是指,提高某个占有某种资源的低优先级线程的优先级,使之与所有等待该资源的线程中优先级最高的那个线程的优先级相等,然后执行,而当这个低优先级线程释放该资源时,优先级重新回到初始设定。因此,继承优先级的线程避免了系统资源被任何中间优先级的线程抢占。

注:针对优先级继承算法,低优先级线程在获得互斥量后要尽快释放互斥量,并且在持有互斥量的过程中,不得再行更改持有互斥量线程的优先级

 

(三)死锁

1、单一线程递归持有信号量造成的自死锁

某一个线程持有一信号量,并递归调用了该信号量,造成信号量的值为0后,线程停下无法运行(相当于该线程被自己锁住了)。

2、两个线程相互死锁

使用互斥锁的时候,两个线程分别开启了一把锁,造成两个线程相互死锁

(四)递归锁

foo函数和bar函数都获取了同一个锁,而bar函数又会调用foo函数。如果MutexLock锁是个非递归锁,则这个程序会立即死锁。因此在为一段程序加锁时要格外小心,否则很容易因为这种调用关系而造成死锁。不要存在侥幸心理,觉得这种情况是很少出现的。当代码复杂到一定程度,被多个人维护,调用关系错综复杂时,程序中很容易犯这样的错误。庆幸的是,这种原因造成的死锁很容易被排除。
但是这并不意味着应该用递归锁去代替非递归锁递归锁用起来固然简单,但往往会隐藏某些代码问题。比如调用函数和被调用函数以为自己拿到了锁,都在修改同一个对象,这时就很容易出现问题。因此在能使用非递归锁的情况下,应该尽量使用非递归锁,因为死锁相对来说,更容易通过调试发现。程序设计如果有问题,应该暴露的越早越好。

 

MutexLock mutex;  

void foo()  
{  
    mutex.lock();  
    // do something  
    mutex.unlock();  
}  

void bar()  
{  
    mutex.lock();  
    // do something  
    foo();  
    mutex.unlock();   
}  

 

 

 

 

 

 

 


http://www.niftyadmin.cn/n/1252752.html

相关文章

线程同步之事件集event

一、事件集 一个事件集中包含 32 个事件(一个 32 bit 无符号整型数),特定线程只等待、接收它关注的事件。可以是一个线程等待多个事件的到来(线程 1、2 均等待多个事件,事件间可以使用 “与” 或者 “或” 逻辑触发线…

电源引脚VDD、VSS、VDDA、VSSA、VREF+、VREF-、VBAT

总体电源框图 一、名词解释 名称说明备注VCC电路的供电正电压一般表示模拟信号电源GND电路的供电负电压?表示模拟信号地VDD芯片的工作正电压表示数字信号电源VSS芯片的工作负电压表示数字电源地VDDA芯片的工作正电压(模拟)VSSA芯片的工作负电压(模拟)VDDD芯片的工作…

norflash和nandflash;SRAM和SDRAM

一、常见存储类型 典型应用名称描述MCU片内flashnorflash nor flash数据线和地址线分开,可以实现ram一样的随机寻址功能,可以读取任何一个字节。但是擦除仍要按块(页)擦除。 因为按照字节寻址,所以程序可以在nor flash中运行。嵌入式系统多用…

断言ASSERT使用详解

一、assert的定义 原以为assert 仅仅是个报错函数,事实上,它居然是个宏,并且作用并非"报错"在经过对其进行一定了解之后,对其作用及用法有了一定的了解,assert() 的用法像是一种"契约式编程"&…

快速排序qsort函数用法

一、qsort函数简介 排序方法有很多种:选择排序,冒泡排序,归并排序,快速排序等。 看名字都知道快速排序是目前公认的一种比较好的排序算法。因为他速度很快,所以系统也在库里实现这个算法,便于我们的使用。…

调度器锁rt_enter_critical() rt_exit_critical()

一、函数说明 void rt_enter_critical(void); /* 进入临界区*/ 调用这个函数后,调度器将被上锁。在系统锁住调度器的期间,系统依然响应中断,如果中断唤醒了的更高优先级线程,调度器并不会立刻执行它,直到调用解锁调度…

RT Thread studio同时生成bin和hex文件

一、RTT默认生成bin文件 默认选择Raw binary,则项目对应的debug文件夹下生成bin文件; 修改成intel的话,则项目对应的debug文件夹下生产hex文件; 二、同时生成bin和hex文件 项目 属性-C/C构建-设置-构建步骤-命令 arm-none-ea…

使用CJSON 解析JSON 结构体数组【典型】

1.CJSON数据结构定义 #define cJSON_False 0 #define cJSON_True 1 #define cJSON_NULL 2 #define cJSON_Number 3 #define cJSON_String 4 #define cJSON_Array 5 //数组 #define cJSON_Object 6 //对象or单键名typedef struct cJSON {struct cJSON *next,*prev; /*遍历数…