精华内容
下载资源
问答
  • Java互斥锁简单实例

    2020-09-03 13:27:19
    主要介绍了Java互斥锁,较为详细的分析了java互斥锁的概念与功能,并实例描述了java互斥锁的原理与使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
  • 而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起配合使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。...
  • 互斥锁与事件锁

    2018-02-26 10:00:55
    里面详细介绍了互斥锁与事件锁,内DEMO,并介绍了事件锁的两种方式的对比
  • C++ 互斥锁源码

    2017-09-01 11:33:02
    ConsoleApp_Mutex,C++互斥锁源码cpp,可在VC++6.0或VS下直接编译运行,演示结果,控制台程序,ConsoleApp_Mutex,C++互斥锁源码cpp,可在VC++6.0或VS下直接编译运行,演示结果,控制台程序,
  • 主要为大家详细介绍了C#多线程中如何运用互斥锁Mutex,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 该例程是参考FreeRTOS官方说明自己编写的代码,内部包含MDK工程源码,希望能帮助到初学FreeRTOS的学子。
  • 互斥锁死锁状态详解

    千次阅读 2017-06-01 01:53:16
    每个对象都对应于一个可称为” 互斥锁” 的标记,这个标记用来保证在任一时刻,只能一个线程访问该对象。那么到底怎样的情形才会产生死锁呢? 典型的两种死锁情形: (一)线程自己将自己锁住 一般情况下,如果...

    在编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为” 互斥锁” 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。那么到底怎样的情形才会产生死锁呢?
    典型的两种死锁情形:
    (一)线程自己将自己锁住
    一般情况下,如果同一个线程先后两次调用lock,在第二次调⽤用时,由于锁已经被占用,该线程会挂起等待占用锁的线程释放锁,然而锁正是被自己占用着的,该线程又被挂起而没有机会释放锁,因此 就永远处于挂起等待状态了,于是就形成了死锁(Deadlock)。
    (二)多线程抢占锁资源被困
    又如线程A获 得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起等待线程B释放 锁2,而这时线程B也调用lock试图获得锁1,结果是需要挂起等待线程A释放锁1,于是线程A和B都 永远处于挂起状态了,死锁再次形成。

    二、计算机系统中的死锁

    1、资源分类
    (一)可重用性资源和消耗性资源
    可重用资源:可供用户重复使用多次的资源。
    性质:
    1》每一个可重用性资源中的单元只能分配给一个进程(或线程)使用,不允许多个进程(或线程)共享。
    2》可重用性资源的使用顺序:
    请求资源—->使用资源—->释放资源
    3》系统中每一类可重用性资源中的单元数目是相对固定的,进程(或线程)在运行期间既不能创建也不能删除它。

    可消耗性资源:又称临时性资源,是由进程(或线程)在运行期间动态的创建和消耗的。
    性质:
    1》每一类可消耗性资源的单元数目在进程(或线程)运行期间是可以不断变化的,有时可能为0.
    2》进程,或线程)在运行过程中可以不断的创建可消耗性资源的单元,将它们放入该资源类的缓冲区中,以增加该资源类的单元数目。
    3》进程(或线程)在运行过程中可请求若干个可消耗性资源,用于进程(或线程)自己的消耗不再将它们返回给该资源类中。
    可消耗性资源通常是由生产者进程(或线程)创建,由消费者进程(或线程)消耗。

    (二)可抢占性资源和不可抢占性资源
    可抢占性资源:☞某进程(或线程)在获得该类资源后,该资源可以被其他进程(或线程)或系统抢占。
    CPU和主存均属于可抢占性资源。
    不可抢占性资源:☞系统一旦把某资源分配该进程(或线程)之后,就不能强行收回,只能在进程(或线程)用完之后自行释放。
    磁带机、打印机等都属于不可抢占性资源。

    2、引起死锁的原因
    (一)竞争不可抢占资源引起死锁
    如:共享文件时引起死锁
    这里写图片描述

    系统中拥有两个进程P1和P2,它们都准备写两个文件F1和F2。而这两者都属于可重用和不可抢占性资源。如果进程P1在打开F1的同时,P2进程打开F2文件,当P1想打开F2时由于F2已结被占用而阻塞,当P2想打开1时由于F1已结被占用而阻塞,此时就会无线等待下去,形成死锁。

    (二)竞争可消耗资源引起死锁
    如:进程通信时引起死锁

    这里写图片描述

    系统中拥有三个进程P1、P2和P3,m1、m2、m3是3可消耗资源。进程P1一方面产生消息m1,将其发送给P2,另一方面要从P3接收消息m3。而进程P2一方面产生消息m2,将其发送给P3,另一方面要从P1接收消息m1。类似的,进程P3一方面产生消息m3,将其发送给P1,另一方面要从P2接收消息m2。
    如果三个进程都先发送自己产生的消息后接收别人发来的消息,则可以顺利的运行下去不会产生死锁,但要是三个进程都先接收别人的消息而不产生消息则会永远等待下去,产生死锁。

    (三)进程推进顺序不当引起死锁

    这里写图片描述
    上图中,如果按曲线1的顺序推进,两个进程可顺利完成;如果按曲线2的顺序推进,两个进程可顺利完成;如果按曲线3的顺序推进,两个进程可顺利完成;如果按曲线4的顺序推进,两个进程将进入不安全区D中,此时P1保持了资源R1,P2保持了资源R2,系统处于不安全状态,如果继续向前推进,则可能产生死锁。

    三、死锁的定义、必要条件和处理方法

    1、死锁的定义:如果一组进程(或线程)中的每一个进程(或线程)都在等待仅由该组进程中的其他进程(或线程)才能引发的事件,那么该组进程(或线程)是死锁的(Deadlock)。
    2、产生死锁的必要条件
    (1)互斥条件。进程(线程)所申请的资源在一段时间内只能被一个进程(线程)锁占用。
    (2)请求和保持条件。进程(线程)已经占有至少一个资源,但又提出了新的资源请求,而该资源却被其他进程(线程)占用。
    (3)不可抢占条件(不可剥夺条件)。进程(线程)已获得的资源在未使用完之前不能被抢占。
    (4)循环等待条件(环路等待条件)。在发生死锁时,必然存在一个进程(线程)—-资源的循环链。
    3、处理死锁的方法
    (1)预防死锁。破坏死锁产生的必要条件中的一个或多个。注意,互斥条件不能被破坏,否则会造成结果的不可再现性。
    (2)避免死锁。在资源分匹配过程中,防止系统进入不安全区域。
    (3)检测死锁。通过检测机构检测死锁的发生,然后采取适当措施解除死锁。
    (4)解除死锁。在检测机构检测死锁发生后,采取适当措施解除死锁。

    四、利用银行家算法避免死锁

    1、银行家算法中的数据结构
    (1)可利用资源向量Available[m]。m为系统中的资源种类数,如果向量Available[j] = K,则表示系统中Rj类资源由K个。
    (2)最大需求矩阵Max[n][m]。m为系统中的资源种类数,n为系统中正在运行的进程(线程)数,如果Max[i][j] = K,则表示进程i需要Rj类资源的最大数目为K个。
    (3)分配矩阵Allocation[n][m]。m为系统中的资源种类数,n为系统中正在运行的进程(线程)数,如果Allocation[i][j] = K,则表示进程i当前已分得Rj类资源的数目为K个。
    (4)需求矩阵Need[n][m]。m为系统中的资源种类数,n为系统中正在运行的进程(线程)数,如果Need[i][j] = K,则表示进程i还需要Rj类资源K个。
    以上三个矩阵间的关系:
    Need[i][j] = Max[i][j] - Allocation[i][j]

    2、银行家算法
    设Request( i)是进程Pi的请求向量,如果Request(i) [j] = K,表示进程Pi需要K个Rj类型的资源。
    (1)如果Request(i) [j] <= Need[i][j],转向步骤(2)。
    (2)如果Request(i) [j] <= Available[j] ,转向步骤(3)。
    (3)系统尝试着把资源分给进程Pi。
    Available[j] = Available[j] - Request(i) [j];
    Allocation[i][j] = Allocation[i][j] + Request(i) [j];
    Need[i][j] = Need[i][j] - Request(i) [j];
    (4)系统执行安全性算法,检查此次资源分配后系统是否处于安全状态。

    3、安全性算法
    (1)设置两个向量:
    1》工作向量Work[m],它表示系统可提供给进程继续运行所需要的各类资源数目,初始值Work = Available。
    2》Finish:它表示系统是否有足够的资源分配给进程,使其运行完成。开始时Finish[i] = false,当有足够的资源分配给进程时Finish[i] = true。
    (2)从进程(线程)集合中找到一个能满足下述条件的进程(线程)。
    1》Finish[i] = false
    2》Need[i][j] <= Work[j],如果找到转到步骤3》,没找到转到步骤4》。
    3》Work[j] = Work[j] + Allocation[i][j] ;
    Finish[i] = true;
    go to step 2;
    4》如果所有进程(线程)的Finish[i] = true都满足,表示系统处于安全状态,反之系统处于不安全状态。

    展开全文
  • 主要介绍了简单的JavaScript互斥锁的相关资料,需要的朋友可以参考下
  • 主要介绍了Python实现的多线程同步与互斥锁功能,涉及Python多线程及锁机制相关操作技巧,需要的朋友可以参考下
  • 主要介绍了Java使用synchronized实现互斥锁功能,结合实例形式分析了Java使用synchronized互斥锁功能简单实现方法与操作技巧,需要的朋友可以参考下
  • vxworks 下互斥锁

    2014-07-01 14:29:27
    讲述了vxworks 互斥锁的应用,由于资料较少,这还是比较难得的
  • 自旋锁和互斥锁区别

    2020-07-18 05:33:26
    线程同步(Thread Synchronization)是并行编程中非常重要的通讯手段,其中最典型的应用就是用Pthreads提供的机制(lock)来对多个线程之间共 享的临界区(Critical Section)进行保护(另一种常用的同步机制是barrier)。
  • Redis 互斥锁 Ruby 中使用 Redis 的分布式互斥锁。 支持阻塞和非阻塞语义。 这个想法来自。 概要 在下面的例子中,一次只有一个线程/进程/服务器可以进入锁定块。 RedisMutex . with_lock ( :your_lock_name ) ...
  • 线程与互斥锁的应用

    2015-01-04 17:00:43
    用两个线程,一个线程echo,一个线程cat来读取手机的imei
  • 信号量与互斥锁的示例代码,参考书籍《深入理解计算机系统》
  • linux和win32下通用的互斥锁Mutex封装,统一接口,可直接使用
  • 互斥锁示例代码

    2015-06-14 22:40:50
    Linux系统编程——线程同步与互斥:互斥锁,相关教程链接如下: http://blog.csdn.net/tennysonsky/article/details/46494077
  • 主要介绍了PHP程序中的文件锁、互斥锁、读写锁使用技巧解析,其中重点讲解了sync模块和pthreads模块中的使用实例,需要的朋友可以参考下
  • 主要介绍了Python的互斥锁与信号量详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 互斥锁

    2019-10-23 10:59:17
    1、互斥锁基本原理: 互斥锁是一个二元变量,其状态为开锁(允许0)和上锁(禁止1),将某个共享资源与某个特定互斥锁在逻辑上绑定(要申请该资源必须先获取锁)。 (1)访问公共资源前,必须申请该互斥锁,若处于开锁状态...

    互斥锁

    线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性

    1、互斥锁基本原理:
    互斥锁是一个二元变量,其状态为开锁(允许0)和上锁(禁止1),将某个共享资源与某个特定互斥锁在逻辑上绑定(要申请该资源必须先获取锁)。
    (1)访问公共资源前,必须申请该互斥锁,若处于开锁状态,则申请到锁对象,并立即占有该锁,以防止其他线程访问该资源;如果该互斥锁处于锁定状态,则阻塞当前线程。
    (2)只有锁定该互斥锁的进程才能释放该互斥锁,其他线程试图释放无效。

    2、初始化互斥锁:
    使用之前,需要定义互斥锁,使用函数:pthread_mutex_t lock;进行定义

    extern int    pthread_mutex_init(pthread_mutex_t * **_mutex**,_const pthread_mutex_mutexattr_t*  **_mutexattr**)
    

    参数: _mutex 初始化的互斥锁的指针
    _mutexattr 指向对象的指针,若为空则默认属性

    3、申请互斥锁
    如果一个线程要占用共享资源,必须先申请对应互斥锁,使用函数:
    以阻塞方式申请互斥锁:

    extern int pthread_mutex_lock(pthread_mutex* _mutex)
    

    以非阻塞方式申请互斥锁:

    extern int pthread_mutex_trylock(pthread_mutex* _mutex
    

    4、释放互斥锁
    释放互斥锁函数:

    extern int pthread_mutex_unlock(pthread_mutex_t* _mutex)
    

    死锁

    死锁是指一个资源被多次调用,而多次调用方都未能释放该资源就会造成一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁

    死锁产生:
    一个线程内部多次加锁缺没有释放引起死锁
    多个线程内部多次调用引起死锁
    python中的实例

    展开全文
  • 主要介绍了Python多线程操作之互斥锁、递归锁、信号量、事件,结合实例形式详细分析了Python多线程操作互斥锁、递归锁、信号量、事件相关概念、原理、用法与操作注意事项,需要的朋友可以参考下
  • FreeRTOS互斥锁

    千次阅读 2019-03-09 23:06:35
    信号量API函数实际上都是宏,它使用现有的队列机制。这些宏定义在semphr.h文件中。如果使用信号量或者互斥...互斥锁和递归互斥锁互斥锁是用来保证共享数据操作的完整性,同时只能一个任务访问共享数据。递归互斥...

    信号量API函数实际上都是宏,它使用现有的队列机制。这些宏定义在semphr.h文件中。如果使用信号量或者互斥量,需要包含semphr.h头文件。

    信号量包括二值信号量、计数信号量、互斥信号量和递归互斥信号量。和普通队列比起来,信号量虽然没有队列项实体,但是信号量值等同于队列项个数。

     

     

    互斥锁和递归互斥锁:互斥锁是用来保证共享数据操作的完整性,同时只能有一个任务访问共享数据。递归互斥锁和普通互斥锁比起来,同一个任务可以多次获取递归互斥锁,在释放同等次数之后才能解锁。

     

     

    在分析互斥锁之前,先搞清楚两个概念,优先级反转和优先级继承

    优先级反转:互斥锁被低优先级的任务持有,高优先级任务等待解锁。这时中等优先级任务一直运行,这导致高优先级任务在等待低优先级任务,而低优先级任务无法执行。这种高优先级等待中优先级的情况,叫做优先级反转。

    优先级继承:为了解决优先级反转而提出优化机制,让低优先级任务临时继承高优先级任务的优先级。在低优先级释放互斥锁之后,还要恢复原来的优先级。

     

     

    先看一下任务TCB

    因为优先级继承机制,在互斥锁释放后需要恢复优先级,uxBasePriority在优先级继承期间被用来保存任务优先级

    uxMutexesHeld表示任务持有了多少个互斥锁

    /* 任务TCB */
    typedef struct tskTaskControlBlock
    {
        volatile StackType_t *pxTopOfStack;		/* 栈顶地址 */
    
        ......
    
        ListItem_t xStateListItem;			/* 状态列表项:运行、就绪、挂起、阻塞 */
        ListItem_t xEventListItem;			/* 事件列表项 */
        UBaseType_t uxPriority;			/* 优先级 */
        StackType_t *pxStack;			/* 栈指针 */
        char pcTaskName[configMAX_TASK_NAME_LEN];	/* 任务名 */
    
        ......
    
    #if (configUSE_MUTEXES == 1)
        UBaseType_t uxBasePriority;		        /* 任务基础优先级 */
        UBaseType_t uxMutexesHeld;		        /* 互斥锁持有数量 */
    #endif
    
        ......
    }tskTCB;
    typedef tskTCB TCB_t;

     

     

    创建互斥锁

     互斥锁实际上是调用了队列的创建函数,创建好之后调用prvInitialiseMutex来初始化一些互斥锁特有的变量

    #define xSemaphoreCreateMutex() xQueueCreateMutex(queueQUEUE_TYPE_MUTEX)
    /* 创建互斥锁 */
    QueueHandle_t xQueueCreateMutex(const uint8_t ucQueueType)
    {
    	QueueHandle_t xNewQueue;
    	const UBaseType_t uxMutexLength = (UBaseType_t)1, uxMutexSize = (UBaseType_t)0;
    
    	/* 创建互斥锁队列 */
    	xNewQueue = xQueueGenericCreate(uxMutexLength, uxMutexSize, ucQueueType);
    	/* 初始化互斥锁队列 */
    	prvInitialiseMutex((Queue_t *)xNewQueue);
    
    	return xNewQueue;
    }

    prvInitialiseMutex函数,将互斥锁初始化为没有被任何任务持有,并且处于解锁状态(队列中有一个队列项)

    /* 初始化互斥锁 */
    static void prvInitialiseMutex(Queue_t *pxNewQueue)
    {
    	if(pxNewQueue != NULL)
    	{
    		/* 初始化互斥锁的持有者为空 */
    		pxNewQueue->u.xSemaphore.xMutexHolder = NULL;
    		/* 初始化队列类型为互斥锁 */
    		pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;
    		/* 初始化递归次数为0 */
    		pxNewQueue->u.xSemaphore.uxRecursiveCallCount = 0;
    
    		traceCREATE_MUTEX(pxNewQueue);
    
    		/* 初始化互斥锁为解锁状态(向队列中发送一个队列项) */
    		(void)xQueueGenericSend(pxNewQueue, NULL, (TickType_t)0U, queueSEND_TO_BACK);
    	}
    	else
    	{
    		traceCREATE_MUTEX_FAILED();
    	}
    }

     

     

    获取互斥锁

    互斥锁也叫互斥信号量,获取互斥锁和获取信号量使用同一个函数

    和普通信号量不同的是:

    1.获取成功时,将互斥锁持有者设为当前任务,当前任务持有互斥锁数量加一

    2.因互斥锁被其它任务持有而阻塞时,为了防止优先级反转现象,进行优先级继承处理

    3.因互斥锁被其它任务持有而阻塞,超时之后,因为可能进行了优先级继承,剥夺原先继承的优先级(剥不剥夺看优先级是否继承自该任务)

    从源代码看,调用xTaskPriorityInherit函数来进行优先级继承;在超时之后,使用prvGetDisinheritPriorityAfterTimeout和vTaskPriorityDisinheritAfterTimeout函数来恢复优先级

    #define xSemaphoreTake(xSemaphore, xBlockTime) xQueueSemaphoreTake((xSemaphore), (xBlockTime))
    /* 获取信号量值 */
    BaseType_t xQueueSemaphoreTake(QueueHandle_t xQueue, TickType_t xTicksToWait)
    {
    	BaseType_t xEntryTimeSet = pdFALSE;
    	TimeOut_t xTimeOut;
    	Queue_t *const pxQueue = xQueue;
    
    #if (configUSE_MUTEXES == 1)
    	BaseType_t xInheritanceOccurred = pdFALSE;
    #endif
    
    	configASSERT((pxQueue));
    
    	configASSERT(pxQueue->uxItemSize == 0);
    
    	#if ((INCLUDE_xTaskGetSchedulerState == 1) || (configUSE_TIMERS == 1))
    	{
    		configASSERT(!((xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) && (xTicksToWait != 0)));
    	}
    	#endif
    
    	for(;;)
    	{
    		/* 进入临界区 */
    		taskENTER_CRITICAL();
    		{
    			/* 信号量计数 */
    			const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting;
    
    			/* 信号量值大于0 */
    			if(uxSemaphoreCount > (UBaseType_t)0)
    			{
    				traceQUEUE_RECEIVE(pxQueue);
    
    				/* 信号量值减一 */
    				pxQueue->uxMessagesWaiting = uxSemaphoreCount - (UBaseType_t)1;
    
    				#if (configUSE_MUTEXES == 1)
    				{
    					/* 队列类型是互斥锁 */
    					if(pxQueue->uxQueueType == queueQUEUE_IS_MUTEX)
    					{
    						/* 互斥锁的持有者设为当前任务,当前任务持有锁的数量加一 */
    						pxQueue->u.xSemaphore.xMutexHolder = pvTaskIncrementMutexHeldCount();
    					}
    					else
    					{
    						mtCOVERAGE_TEST_MARKER();
    					}
    				}
    				#endif
    
    				/* 等待释放信号量而阻塞的任务列表不为空 */
    				if(listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToSend)) == pdFALSE)
    				{
    					/* 将任务从释放信号量而阻塞的任务列表中移除,任务优先级大于当前任务优先级 */
    					if(xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToSend)) != pdFALSE)
    					{
    						/* 请求切换任务 */
    						queueYIELD_IF_USING_PREEMPTION();
    					}
    					else
    					{
    						mtCOVERAGE_TEST_MARKER();
    					}
    				}
    				else
    				{
    					mtCOVERAGE_TEST_MARKER();
    				}
    
    				/* 退出临界区 */
    				taskEXIT_CRITICAL();
    				
    				/* 成功 */
    				return pdPASS;
    			}
    			/* 信号量值为0 */
    			else
    			{
    				/* 等待时间为0 */
    				if(xTicksToWait == (TickType_t)0)
    				{
    					#if (configUSE_MUTEXES == 1)
    					{
    						configASSERT(xInheritanceOccurred == pdFALSE);
    					}
    					#endif
    					
    					/* 退出临界区 */
    					taskEXIT_CRITICAL();
    					traceQUEUE_RECEIVE_FAILED(pxQueue);
    					
    					/* 返回队列为空错误 */
    					return errQUEUE_EMPTY;
    				}
    				/* 没有记录过当前节拍状态 */
    				else if(xEntryTimeSet == pdFALSE)
    				{
    					/* 记录当前节拍状态 */
    					vTaskInternalSetTimeOutState(&xTimeOut);
    					
    					/* 已经记录过当前节拍状态 */
    					xEntryTimeSet = pdTRUE;
    				}
    				else
    				{
    					mtCOVERAGE_TEST_MARKER();
    				}
    			}
    		}
    		/* 退出临界区 */
    		taskEXIT_CRITICAL();
    
    		/* 挂起调度器 */
    		vTaskSuspendAll();
    		
    		/* 锁定队列 */
    		prvLockQueue(pxQueue);
    
    		/* 检查是否超时,没有超时 */
    		if(xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE)
    		{
    			/* 检查信号量值是否为0,为0 */
    			if(prvIsQueueEmpty(pxQueue) != pdFALSE)
    			{
    				traceBLOCKING_ON_QUEUE_RECEIVE(pxQueue);
    
    				#if (configUSE_MUTEXES == 1)
    				{
    					/* 队列类型为互斥锁 */
    					if(pxQueue->uxQueueType == queueQUEUE_IS_MUTEX)
    					{
    						/* 进入临界区 */
    						taskENTER_CRITICAL();
    						{
    							/* 任务优先级继承,返回值是否继承优先级 */
    							xInheritanceOccurred = xTaskPriorityInherit(pxQueue->u.xSemaphore.xMutexHolder);
    						}
    						/* 退出临界区 */
    						taskEXIT_CRITICAL();
    					}
    					else
    					{
    						mtCOVERAGE_TEST_MARKER();
    					}
    				}
    				#endif
    
    				/* 将任务插入等待获取信号量而阻塞的任务列表中 */
    				vTaskPlaceOnEventList(&(pxQueue->xTasksWaitingToReceive), xTicksToWait);
    				/* 解锁队列 */
    				prvUnlockQueue(pxQueue);
    				/* 解除调度器挂起,没有切换过任务 */
    				if(xTaskResumeAll() == pdFALSE)
    				{
    					/* 请求切换任务 */
    					portYIELD_WITHIN_API();
    				}
    				else
    				{
    					mtCOVERAGE_TEST_MARKER();
    				}
    			}
    			/* 队列不为空,while下一个循环时取走队列项 */
    			else
    			{
    				/* 解锁队列 */
    				prvUnlockQueue(pxQueue);
    				/* 解除调度器挂起 */
    				(void)xTaskResumeAll();
    			}
    		}
    		/* 已经超时或者超时之后 */
    		else
    		{
    			/* 解锁队列 */
    			prvUnlockQueue(pxQueue);
    			
    			/* 解除调度器挂起 */
    			(void)xTaskResumeAll();
    
    			/* 检查队列是否为空,为空 */
    			if(prvIsQueueEmpty(pxQueue) != pdFALSE)
    			{
    				#if (configUSE_MUTEXES == 1)
    				{
    					/* 继承了优先级 */
    					if(xInheritanceOccurred != pdFALSE)
    					{
    						/* 进入临界区 */
    						taskENTER_CRITICAL();
    						{
    							UBaseType_t uxHighestWaitingPriority;
    
    							/* 获取剩下所有等待互斥锁任务的最高优先级 */
    							uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout(pxQueue);
    							/* 超时之后剥夺继承优先级 */
    							vTaskPriorityDisinheritAfterTimeout(pxQueue->u.xSemaphore.xMutexHolder, uxHighestWaitingPriority);
    						}
    						/* 退出临界区 */
    						taskEXIT_CRITICAL();
    					}
    				}
    				#endif
    
    				traceQUEUE_RECEIVE_FAILED(pxQueue);
    				
    				/* 返回队列为空错误 */
    				return errQUEUE_EMPTY;
    			}
    			/* 队列不为空,while下一个循环时取走队列项 */
    			else
    			{
    				mtCOVERAGE_TEST_MARKER();
    			}
    		}
    	}
    }

     

     

    释放互斥锁

    从源代码来看,看不出和普通信号量处理有什么区别。

    但是,因为互斥锁可能存在优先级继承的问题,因此释放互斥锁时肯定需要恢复优先级。

    事实上,恢复优先级在prvCopyDataToQueue函数中进行。

    #define xSemaphoreGive(xSemaphore) xQueueGenericSend((QueueHandle_t)(xSemaphore), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK)
    /* 发送队列项 */
    BaseType_t xQueueGenericSend(QueueHandle_t xQueue, const void *const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition)
    {
    	BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
    	TimeOut_t xTimeOut;
    	Queue_t *const pxQueue = xQueue;
    
    	configASSERT(pxQueue);
    	configASSERT(!((pvItemToQueue == NULL) && (pxQueue->uxItemSize != (UBaseType_t)0U)));
    	configASSERT(!((xCopyPosition == queueOVERWRITE) && (pxQueue->uxLength != 1)));
    	
    	#if ((INCLUDE_xTaskGetSchedulerState == 1) || (configUSE_TIMERS == 1))
    	{
    		configASSERT(!((xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) && (xTicksToWait != 0)));
    	}
    	#endif
    
    	for(;;)
    	{
    		/* 进入临界区 */
    		taskENTER_CRITICAL();
    		{
    			/* 目前已插入队列项数小于最大可插入队列数或者覆盖型插入 */
    			if((pxQueue->uxMessagesWaiting < pxQueue->uxLength) || (xCopyPosition == queueOVERWRITE))
    			{
    				traceQUEUE_SEND(pxQueue);
    
    				#if (configUSE_QUEUE_SETS == 1)
    				{
    					UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting;
    
    					xYieldRequired = prvCopyDataToQueue(pxQueue, pvItemToQueue, xCopyPosition);
    
    					if(pxQueue->pxQueueSetContainer != NULL)
    					{
    						if((xCopyPosition == queueOVERWRITE) && (uxPreviousMessagesWaiting != (UBaseType_t)0))
    						{
    							mtCOVERAGE_TEST_MARKER();
    						}
    						else if(prvNotifyQueueSetContainer(pxQueue, xCopyPosition) != pdFALSE)
    						{
    							queueYIELD_IF_USING_PREEMPTION();
    						}
    						else
    						{
    							mtCOVERAGE_TEST_MARKER();
    						}
    					}
    					else
    					{
    						if(listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE)
    						{
    							if(xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE)
    							{
    								queueYIELD_IF_USING_PREEMPTION();
    							}
    							else
    							{
    								mtCOVERAGE_TEST_MARKER();
    							}
    						}
    						else if(xYieldRequired != pdFALSE)
    						{
    							queueYIELD_IF_USING_PREEMPTION();
    						}
    						else
    						{
    							mtCOVERAGE_TEST_MARKER();
    						}
    					}
    				}
    				#else
    				{
    					/* 根据不同的入队方式,将队列项数据拷贝到队列中 */
    					xYieldRequired = prvCopyDataToQueue(pxQueue, pvItemToQueue, xCopyPosition);
    
    					/* 等待接收队列项而阻塞的任务列表不为空 */
    					if(listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE)
    					{
    						/* 将任务从事件列表中移除一个任务,并挂接到就绪列表 */
    						if(xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE)
    						{
    							/* 请求切换任务 */
    							queueYIELD_IF_USING_PREEMPTION();
    						}
    						else
    						{
    							mtCOVERAGE_TEST_MARKER();
    						}
    					}
    					/* 等待接收队列项而阻塞的任务列表为空 */
    					else if(xYieldRequired != pdFALSE)
    					{
    						queueYIELD_IF_USING_PREEMPTION();
    					}
    					else
    					{
    						mtCOVERAGE_TEST_MARKER();
    					}
    				}
    				#endif
    
    				/* 退出临界区 */
    				taskEXIT_CRITICAL();
    
    				/* 成功 */
    				return pdPASS;
    			}
    			/* 目前队列已经满了,且不是覆盖型插入 */
    			else
    			{
    				/* 阻塞时间为0 */
    				if(xTicksToWait == (TickType_t)0)
    				{
    					taskEXIT_CRITICAL();
    
    					traceQUEUE_SEND_FAILED(pxQueue);
    
    					/* 返回队列已满错误 */
    					return errQUEUE_FULL;
    				}
    				/* 当前节拍状态还未记录 */
    				else if(xEntryTimeSet == pdFALSE)
    				{
    					/* 记录当前节拍状态 */
    					vTaskInternalSetTimeOutState(&xTimeOut);
    					/* 当前节拍状态已经记录 */
    					xEntryTimeSet = pdTRUE;
    				}
    				else
    				{
    					mtCOVERAGE_TEST_MARKER();
    				}
    			}
    		}
    		/* 退出临界区 */
    		taskEXIT_CRITICAL();
    
    		/* 挂起调度器 */
    		vTaskSuspendAll();
    
    		/* 锁定队列 */
    		prvLockQueue(pxQueue);
    
    		/* 检查任务是否超时,并未超时 */
    		if(xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE)
    		{
    			/* 检查队列是否已满,已经满了 */
    			if(prvIsQueueFull(pxQueue) != pdFALSE)
    			{
    				traceBLOCKING_ON_QUEUE_SEND(pxQueue);
    				/* 将任务挂接到等待发送而阻塞的任务列表中,并将任务挂接到延时列表中 */
    				vTaskPlaceOnEventList(&(pxQueue->xTasksWaitingToSend), xTicksToWait);
    				/* 解锁队列 */
    				prvUnlockQueue(pxQueue);
    
    				/* 解除调度器挂起 */
    				if(xTaskResumeAll() == pdFALSE)
    				{
    					/* 请求切换 */
    					portYIELD_WITHIN_API();
    				}
    			}
    			/* 刚好队列出现空位,下一次while循环重新插入 */
    			else
    			{
    				/* 解锁队列 */
    				prvUnlockQueue(pxQueue);
    				/* 解除调度器挂起 */
    				(void)xTaskResumeAll();
    			}
    		}
    		/* 已经超时或者超时之后 */
    		else
    		{
    			/* 解锁队列 */
    			prvUnlockQueue(pxQueue);
    			/* 解除调度器挂起 */
    			(void)xTaskResumeAll();
    
    			traceQUEUE_SEND_FAILED(pxQueue);
    			
    			/* 队列已满 */
    			return errQUEUE_FULL;
    		}
    	}
    }

    下面看一下prvCopyDataToQueue函数,看看是怎么恢复优先级的

    从源代码来看是通过xTaskPriorityDisinherit函数来恢复优先级

    /* 将队列项拷贝到队列中 */
    static BaseType_t prvCopyDataToQueue(Queue_t *const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition)
    {
    	BaseType_t xReturn = pdFALSE;
    	UBaseType_t uxMessagesWaiting;
    
    	/* 队列中队列项数 */
    	uxMessagesWaiting = pxQueue->uxMessagesWaiting;
    
    	/* 队列项大小为0 */
    	if(pxQueue->uxItemSize == (UBaseType_t)0)
    	{
    		#if (configUSE_MUTEXES == 1)
    		{
    			/* 队列类型为互斥锁 */
    			if(pxQueue->uxQueueType == queueQUEUE_IS_MUTEX)
    			{
    				/* 剥夺继承优先级 */
    				xReturn = xTaskPriorityDisinherit(pxQueue->u.xSemaphore.xMutexHolder);
    				/* 互斥锁持有者设置为空 */
    				pxQueue->u.xSemaphore.xMutexHolder = NULL;
    			}
    			else
    			{
    				mtCOVERAGE_TEST_MARKER();
    			}
    		}
    		#endif
    	}
    	/* 队列项大小不为0,从队列尾部插入 */
    	else if(xPosition == queueSEND_TO_BACK)
    	{
    		/* 将队列项插入队列 */
    		(void)memcpy((void *)pxQueue->pcWriteTo, pvItemToQueue, (size_t)pxQueue->uxItemSize);
    		/* 将队列项写入指针向后偏移一个队列项 */
    		pxQueue->pcWriteTo += pxQueue->uxItemSize;
    		
    		/* 队列项写入指针已经偏移到队列尾部了 */
    		if(pxQueue->pcWriteTo >= pxQueue->u.xQueue.pcTail)
    		{
    			/* 将队列项写入指针偏移到头部 */
    			pxQueue->pcWriteTo = pxQueue->pcHead;
    		}
    		else
    		{
    			mtCOVERAGE_TEST_MARKER();
    		}
    	}
    	/* 队列项大小不为0,从队列头部插入/覆盖插入 */
    	else
    	{
    		/* 将队列项插入队列 */
    		(void)memcpy((void *)pxQueue->u.xQueue.pcReadFrom, pvItemToQueue, (size_t)pxQueue->uxItemSize);
    		/* 将队列项读出指针向前偏移一个队列项 */
    		pxQueue->u.xQueue.pcReadFrom -= pxQueue->uxItemSize;
    		
    		/* 队列项读出指针已经偏移到队列头部了 */
    		if(pxQueue->u.xQueue.pcReadFrom < pxQueue->pcHead)
    		{
    			/* 将队列项读出指针偏移到尾部 */
    			pxQueue->u.xQueue.pcReadFrom = (pxQueue->u.xQueue.pcTail - pxQueue->uxItemSize);
    		}
    		else
    		{
    			mtCOVERAGE_TEST_MARKER();
    		}
    
    		/* 覆盖式插入 */
    		if(xPosition == queueOVERWRITE)
    		{
    			/* 队列项个数大于0 */
    			if(uxMessagesWaiting > (UBaseType_t)0)
    			{
    				/* 因为覆盖了一个队列项,所以队列项数减一 */
    				--uxMessagesWaiting;
    			}
    			else
    			{
    				mtCOVERAGE_TEST_MARKER();
    			}
    		}
    		else
    		{
    			mtCOVERAGE_TEST_MARKER();
    		}
    	}
    
    	/* 队列项数加一 */
    	pxQueue->uxMessagesWaiting = uxMessagesWaiting + (UBaseType_t)1;
    
    	return xReturn;
    }

     

     

    创建递归互斥锁

    递归互斥锁和互斥锁的创建过程是一样的,只是互斥锁的类型不同

    #define xSemaphoreCreateMutex() xQueueCreateMutex(queueQUEUE_TYPE_MUTEX)

     

     

    获取递归互斥锁

    和互斥锁不同的是,同一任务可以多次获取递归互斥锁。因此,主要步骤为:

    1.如果当前任务不持有该互斥锁,则申请持有互斥锁,递归次数加一

    2.如果当前任务已经持有该互斥锁,则直接递归次数加一 

    /* 获取递归互斥锁 */
    BaseType_t xQueueTakeMutexRecursive(QueueHandle_t xMutex, TickType_t xTicksToWait)
    {
    	BaseType_t xReturn;
    	Queue_t *const pxMutex = (Queue_t *)xMutex;
    
    	configASSERT(pxMutex);
    
    	traceTAKE_MUTEX_RECURSIVE(pxMutex);
    
    	/* 互斥锁持有者为当前任务 */
    	if(pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle())
    	{
    		/* 递归次数加一 */
    		(pxMutex->u.xSemaphore.uxRecursiveCallCount)++;
    		xReturn = pdPASS;
    	}
    	/* 互斥锁持有者不为当前任务 */
    	else
    	{
    		/* 持有该互斥锁 */
    		xReturn = xQueueSemaphoreTake(pxMutex, xTicksToWait);
    		if(xReturn != pdFAIL)
    		{
    			/* 递归次数加一 */
    			(pxMutex->u.xSemaphore.uxRecursiveCallCount)++;
    		}
    		else
    		{
    			traceTAKE_MUTEX_RECURSIVE_FAILED(pxMutex);
    		}
    	}
    
    	return xReturn;
    }

     

     

    释放递归互斥锁

    和互斥锁不同的是,同一任务可以多次获取递归互斥锁,解除同等次数才能彻底释放。因此,主要步骤为:

    1.递归次数减一

    2.当递归次数减完,彻底释放互斥锁

    /* 释放递归互斥锁 */
    BaseType_t xQueueGiveMutexRecursive(QueueHandle_t xMutex)
    {
    	BaseType_t xReturn;
    	Queue_t *const pxMutex = (Queue_t *)xMutex;
    
    	configASSERT(pxMutex);
    
    	/* 互斥锁持有者为当前任务 */
    	if(pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle())
    	{
    		traceGIVE_MUTEX_RECURSIVE(pxMutex);
    
    		/* 递归次数减一 */
    		(pxMutex->u.xSemaphore.uxRecursiveCallCount)--;
    
    		/* 递归次数减到0 */
    		if(pxMutex->u.xSemaphore.uxRecursiveCallCount == (UBaseType_t)0)
    		{
    			/* 彻底释放互斥锁 */
    			(void)xQueueGenericSend(pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK);
    		}
    		else
    		{
    			mtCOVERAGE_TEST_MARKER();
    		}
    
    		xReturn = pdPASS;
    	}
    	/* 互斥锁持有者不为当前任务,直接返回错误 */
    	else
    	{
    		xReturn = pdFAIL;
    
    		traceGIVE_MUTEX_RECURSIVE_FAILED(pxMutex);
    	}
    
    	return xReturn;
    }

     

     

    优先级继承和剥夺

    从上面的分析,我们知道优先级继承和剥夺函数如下

    优先级继承:xTaskPriorityInherit

    互斥锁阻塞超时后,恢复优先级:prvGetDisinheritPriorityAfterTimeout和vTaskPriorityDisinheritAfterTimeout

    解放互斥锁,恢复优先级:xTaskPriorityDisinherit

     

    下面我们一个一个来分析,首先是xTaskPriorityInherit

    1.当前任务优先级和互斥锁持有任务优先级进行比较,将互斥锁持有任务的优先级更新为更高的那个优先级

    2.将互斥锁持有任务按照新的优先级重新放到就绪列表中

    /* 任务优先级继承 */
    BaseType_t xTaskPriorityInherit(TaskHandle_t const pxMutexHolder)
    {
    	TCB_t *const pxMutexHolderTCB = pxMutexHolder;
    	BaseType_t xReturn = pdFALSE;
    
    	/* 互斥锁持有者不为空 */
    	if(pxMutexHolder != NULL)
    	{
    		/* 互斥锁持有者优先级小于当前任务优先级 */
    		if(pxMutexHolderTCB->uxPriority < pxCurrentTCB->uxPriority)
    		{
    			/* 将互斥锁持有者的事件列表项值(任务优先级)设置为当前任务事件列表项值(任务优先级) */
    			if((listGET_LIST_ITEM_VALUE(&(pxMutexHolderTCB->xEventListItem)) & taskEVENT_LIST_ITEM_VALUE_IN_USE) == 0UL)
    			{
    				listSET_LIST_ITEM_VALUE(&(pxMutexHolderTCB->xEventListItem), (TickType_t)configMAX_PRIORITIES - (TickType_t)pxCurrentTCB->uxPriority);
    			}
    			else
    			{
    				mtCOVERAGE_TEST_MARKER();
    			}
    
    			/* 判断互斥锁持有者是否就绪,就绪 */
    			if(listIS_CONTAINED_WITHIN(&(pxReadyTasksLists[pxMutexHolderTCB->uxPriority]), &(pxMutexHolderTCB->xStateListItem)) != pdFALSE)
    			{
    				/* 将互斥锁持有者从就绪列表中移除,移除完后列表中没有任务 */
    				if(uxListRemove(&(pxMutexHolderTCB->xStateListItem)) == (UBaseType_t)0)
    				{
    					/* 检查就绪列表中是否有任务,如果没有将该优先级从当前任务优先级记录中清除 */
    					taskRESET_READY_PRIORITY(pxMutexHolderTCB->uxPriority);
    				}
    				else
    				{
    					mtCOVERAGE_TEST_MARKER();
    				}
    
    				/* 互斥锁持有者优先级设置为当前任务优先级 */
    				pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority;
    				/* 将互斥锁持有者重新插入新优先级的就绪列表中 */
    				prvAddTaskToReadyList(pxMutexHolderTCB);
    			}
    			/* 互斥锁持有者并不在就绪态 */
    			else
    			{
    				/* 互斥锁持有者优先级设置为当前任务优先级 */
    				pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority;
    			}
    
    			traceTASK_PRIORITY_INHERIT(pxMutexHolderTCB, pxCurrentTCB->uxPriority);
    
    			/* 返回继承了优先级 */
    			xReturn = pdTRUE;
    		}
    		/* 互斥锁持有者优先级不小于当前任务优先级 */
    		else
    		{
    			/* 互斥锁持有者基础优先级小于当前任务优先级,说明继承了其它任务优先级 */
    			if(pxMutexHolderTCB->uxBasePriority < pxCurrentTCB->uxPriority)
    			{
    				/* 返回继承了优先级 */
    				xReturn = pdTRUE;
    			}
    			/* 没有继承优先级 */
    			else
    			{
    				mtCOVERAGE_TEST_MARKER();
    			}
    		}
    	}
    	else
    	{
    		mtCOVERAGE_TEST_MARKER();
    	}
    
    	return xReturn;
    }

     

    prvGetDisinheritPriorityAfterTimeout和vTaskPriorityDisinheritAfterTimeout

    用法

    UBaseType_t uxHighestWaitingPriority;
    
    /* 获取剩下所有等待互斥锁任务的最高优先级 */
    uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout(pxQueue);
    /* 超时之后剥夺继承优先级 */
    vTaskPriorityDisinheritAfterTimeout(pxQueue->u.xSemaphore.xMutexHolder, uxHighestWaitingPriority);

    prvGetDisinheritPriorityAfterTimeout获取超时后可能需要剥夺的优先级

    1.获取等待互斥锁阻塞任务中的最高优先级

    /* 获取剩下所有等待互斥锁任务的最高优先级 */
    static UBaseType_t prvGetDisinheritPriorityAfterTimeout(const Queue_t *const pxQueue)
    {
    	UBaseType_t uxHighestPriorityOfWaitingTasks;
    
    	/* 因为拿不到互斥锁而阻塞的任务列表不为空 */
    	if(listCURRENT_LIST_LENGTH(&(pxQueue->xTasksWaitingToReceive)) > 0U)
    	{
    		/* 获取队列中优先级最高的任务 */
    		uxHighestPriorityOfWaitingTasks = (UBaseType_t)configMAX_PRIORITIES - (UBaseType_t)listGET_ITEM_VALUE_OF_HEAD_ENTRY(&(pxQueue->xTasksWaitingToReceive));
    	}
    	/* 因为拿不到互斥锁而阻塞的任务列表为空 */
    	else
    	{
    		/* 最高的优先级为空闲任务优先级 */
    		uxHighestPriorityOfWaitingTasks = tskIDLE_PRIORITY;
    	}
    
    	return uxHighestPriorityOfWaitingTasks;
    }

    vTaskPriorityDisinheritAfterTimeout超时后剥夺优先级

    1.先计算剥夺继承当前任务优先级后,需要重新设置的继承优先级

    2.检查互斥锁持有者的优先级是否继承自当前任务

    3.如果互斥锁持有者的优先级是否继承自当前任务,那么就需要重新设置的继承优先级

    4.设置新的优先级,并将互斥锁持有者从原就绪列表中转移到新的就绪列表中

    注意:FreeRTOS中处理的并不完美,如果互斥锁持有者如果持有不止一把锁的话,即使互斥锁持有者的优先级是否继承自当前任务也不进行剥夺,这可能导致互斥锁的持有任务优先级并不完全准确。

    /* 超时之后剥夺继承优先级 */
    void vTaskPriorityDisinheritAfterTimeout(TaskHandle_t const pxMutexHolder, UBaseType_t uxHighestPriorityWaitingTask)
    {
    	TCB_t *const pxTCB = pxMutexHolder;
    	UBaseType_t uxPriorityUsedOnEntry, uxPriorityToUse;
    	const UBaseType_t uxOnlyOneMutexHeld = (UBaseType_t)1;
    
    	/* 互斥锁持有者不为空 */
    	if(pxMutexHolder != NULL)
    	{
    		configASSERT(pxTCB->uxMutexesHeld);
    
    		/* 互斥锁持有者基础优先级小于等待持有互斥锁任务的最高优先级,说明需要继承优先级 */
    		if(pxTCB->uxBasePriority < uxHighestPriorityWaitingTask)
    		{
    			/* 将要被设置的优先级 */
    			uxPriorityToUse = uxHighestPriorityWaitingTask;
    		}
    		/* 不需要继承优先级 */
    		else
    		{
    			/* 将要被设置的优先级 */
    			uxPriorityToUse = pxTCB->uxBasePriority;
    		}
    
    		/* 互斥锁持有者优先级不等于剩下任务最高优先级,说明之前互斥锁持有者优先级继承于当前任务 */
    		if(pxTCB->uxPriority != uxPriorityToUse)
    		{
    			/* 互斥锁持有者只持有一个互斥锁 */
    			if(pxTCB->uxMutexesHeld == uxOnlyOneMutexHeld)
    			{
    				configASSERT(pxTCB != pxCurrentTCB);
    
    				traceTASK_PRIORITY_DISINHERIT(pxTCB, pxTCB->uxBasePriority);
    				
    				/* 保存任务优先级 */
    				uxPriorityUsedOnEntry = pxTCB->uxPriority;
    				/* 互斥锁持有者优先级设置为剩下等待互斥锁任务的最高优先级 */
    				pxTCB->uxPriority = uxPriorityToUse;
    
    				/* 将事件列表值也改成剩下等待互斥锁任务的最高优先级 */
    				if((listGET_LIST_ITEM_VALUE(&(pxTCB->xEventListItem)) & taskEVENT_LIST_ITEM_VALUE_IN_USE) == 0UL)
    				{
    					listSET_LIST_ITEM_VALUE(&(pxTCB->xEventListItem), (TickType_t)configMAX_PRIORITIES - (TickType_t)uxPriorityToUse);
    				}
    				else
    				{
    					mtCOVERAGE_TEST_MARKER();
    				}
    
    				/* 把任务从当前就绪列表中移除 */
    				if(listIS_CONTAINED_WITHIN(&(pxReadyTasksLists[uxPriorityUsedOnEntry] ), &(pxTCB->xStateListItem)) != pdFALSE)
    				{
    					if(uxListRemove(&(pxTCB->xStateListItem)) == (UBaseType_t)0)
    					{
    						taskRESET_READY_PRIORITY(pxTCB->uxPriority);
    					}
    					else
    					{
    						mtCOVERAGE_TEST_MARKER();
    					}
    
    					/* 重新添加到新优先级就绪列表中 */
    					prvAddTaskToReadyList(pxTCB);
    				}
    				else
    				{
    					mtCOVERAGE_TEST_MARKER();
    				}
    			}
    			else
    			{
    				mtCOVERAGE_TEST_MARKER();
    			}
    		}
    		else
    		{
    			mtCOVERAGE_TEST_MARKER();
    		}
    	}
    	else
    	{
    		mtCOVERAGE_TEST_MARKER();
    	}
    }

     

    xTaskPriorityDisinherit,剥夺优先级继承

    1.先判断是否继承了优先级

    2.如果继承了优先级,恢复基础优先级,并将任务根据新优先级从原就绪列表中转移到新的就绪列表

    注意:FreeRTOS中处理的并不完美,如果互斥锁持有者如果持有不止一把锁的话,并不会剥夺继承优先级,这可能导致互斥锁的持有任务优先级并不完全准确。

    /* 剥夺继承优先级 */
    BaseType_t xTaskPriorityDisinherit(TaskHandle_t const pxMutexHolder)
    {
    	TCB_t *const pxTCB = pxMutexHolder;
    	BaseType_t xReturn = pdFALSE;
    
    	/* 互斥锁持有者不为空 */
    	if(pxMutexHolder != NULL)
    	{
    		configASSERT(pxTCB == pxCurrentTCB);
    		configASSERT(pxTCB->uxMutexesHeld);
    		
    		/* 任务持有锁数量减一 */
    		(pxTCB->uxMutexesHeld)--;
    
    		/* 任务优先级不等于基础优先级,说明继承了优先级 */
    		if(pxTCB->uxPriority != pxTCB->uxBasePriority)
    		{
    			/* 任务持有锁数量为0 */
    			if(pxTCB->uxMutexesHeld == (UBaseType_t)0)
    			{
    				/* 将互斥锁持有者从就绪列表中移除 */
    				if(uxListRemove(&(pxTCB->xStateListItem)) == (UBaseType_t)0)
    				{
    					/* 检查就绪列表中是否有任务,如果没有将该优先级从当前任务优先级记录中清除 */
    					taskRESET_READY_PRIORITY(pxTCB->uxPriority);
    				}
    				else
    				{
    					mtCOVERAGE_TEST_MARKER();
    				}
    
    				traceTASK_PRIORITY_DISINHERIT(pxTCB, pxTCB->uxBasePriority);
    				
    				/* 将任务优先级复原为基础优先级 */
    				pxTCB->uxPriority = pxTCB->uxBasePriority;
    
    				/* 将事件列表项值重新设为基础优先级值 */
    				listSET_LIST_ITEM_VALUE(&(pxTCB->xEventListItem), (TickType_t)configMAX_PRIORITIES - (TickType_t)pxTCB->uxPriority);
    				/* 将任务重新加入新优先级任务列表 */
    				prvAddTaskToReadyList(pxTCB);
    
    				xReturn = pdTRUE;
    			}
    			else
    			{
    				mtCOVERAGE_TEST_MARKER();
    			}
    		}
    		else
    		{
    			mtCOVERAGE_TEST_MARKER();
    		}
    	}
    	else
    	{
    		mtCOVERAGE_TEST_MARKER();
    	}
    
    	return xReturn;
    }

     

     

    展开全文
  • 本篇文章是对Linux下一个简单的多线程互斥锁的例子进行了分析介绍,需要的朋友可以参考下
  • Linux 4种互斥锁

    千次阅读 2017-07-17 22:44:27
    linux 互斥锁

    1.互斥锁类型

    1.普通锁 (PTHREAD_MUTEX_NORMAL)

    • 互斥锁默认类型。当一个线程对一个普通锁加锁以后,其余请求该锁的线程将形成一个等待队列,并在该锁解锁后按照优先级获得它,这种锁类型保证了资源分配的公平性。一个线程如果对一个已经加锁的普通锁再次加锁,将引发死锁;
    • 对一个已经被其他线程加锁的普通锁解锁,或者对一个已经解锁的普通锁再次解锁,将导致不可预期的后果。

    2.检错锁(PTHREAD_MUTEX_ERRORCHECK)

    • 一个线程如果对一个已经加锁的检错锁再次加锁,则加锁操作返回EDEADLK;
    • 对一个已经被其他线程加锁的检错锁解锁或者对一个已经解锁的检错锁再次解锁,则解锁操作返回EPERM;


    3.嵌套锁(PTHREAD_MUTEX_RECURSIVE)

    • 该锁允许一个线程在释放锁之前多次对它加锁而不发生死锁;
    • 其他线程要获得这个锁,则当前锁的拥有者必须执行多次解锁操作;
    • 对一个已经被其他线程加锁的嵌套锁解锁,或者对一个已经解锁的嵌套锁再次解锁,则解锁操作返回EPERM


    4.默认锁(PTHREAD_MUTEX_   DEFAULT)

    • 一个线程如果对一个已经加锁的默认锁再次加锁,或者虽一个已经被其他线程加锁的默认锁解锁,或者对一个解锁的默认锁解锁,将导致不可预期的后果;
    • 这种锁实现的时候可能被映射成上述三种锁之一;


    2.死锁举例

    1.不同线程申请普通锁,将按照顺序依次申请锁并加锁

    #include <pthread.h>
    #include <stdio.h>
    pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
    int count = 0;
    void * thread_func_one(void *arg)
    {
       int i;
       for(i=0;i<10;i++){
         pthread_mutex_lock( &mutex1);
         count++;
         sleep(1);
         pthread_mutex_unlock(&mutex1);
         printf("thread one count value is %d/n",count);
       }
       return NULL;
    }
    void * thread_func_two(void *arg)
    {
       int i;
       for(i=0;i<10;i++){
         pthread_mutex_lock( &mutex1);
         //pthread_mutex_lock( &mutex1);
         count++;
         sleep(1);
         //pthread_mutex_unlock(&mutex1);
    	 pthread_mutex_unlock(&mutex1);
         printf("thread two count value is %d/n",count);
       }
       return NULL;
    }
    int main ( int argc, char **argv)
    {
       pthread_t thread_one, thread_two;
       if( 0!=pthread_create( &thread_one, NULL, thread_func_one,NULL)){
          printf("pthread create failed!/n");
          return -1;
       }
       if( 0!=pthread_create( &thread_one, NULL, thread_func_two,NULL)){
          printf("pthread create failed!/n");
          return -1;
       }
       pthread_join(thread_one, NULL);
       pthread_join(thread_two,NULL);
       return 0;
    }

    2.一个线程对一个普通锁两次加锁将导致死锁

    #include <pthread.h>
    #include <stdio.h>
    pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
    int count = 0;
    void * thread_func_one(void *arg)
    {
       int i;
       for(i=0;i<10;i++){
         pthread_mutex_lock( &mutex1);
         pthread_mutex_lock( &mutex1);//锁两次
         count++;
         sleep(1);
         pthread_mutex_unlock(&mutex1);
         pthread_mutex_unlock(&mutex1);
         printf("thread one count value is %d/n",count);
       }
       return NULL;
    }
    void * thread_func_two(void *arg)
    {
       int i;
       for(i=0;i<10;i++){
         pthread_mutex_lock( &mutex1);
         //pthread_mutex_lock( &mutex1);
         count++;
         sleep(1);
         //pthread_mutex_unlock(&mutex1);
    	 pthread_mutex_unlock(&mutex1);
         printf("thread two count value is %d/n",count);
       }
       return NULL;
    }
    int main ( int argc, char **argv)
    {
       pthread_t thread_one, thread_two;
       if( 0!=pthread_create( &thread_one, NULL, thread_func_one,NULL)){
          printf("pthread create failed!/n");
          return -1;
       }
       if( 0!=pthread_create( &thread_one, NULL, thread_func_two,NULL)){
          printf("pthread create failed!/n");
          return -1;
       }
       pthread_join(thread_one, NULL);
       pthread_join(thread_two,NULL);
       return 0;
    }
    

    3.按不同顺序访问互斥锁导致死锁

    #include <pthread.h>
    #include <stdio.h>
    #include <unistd.h>
    
    int a = 0;
    int b = 0;
    pthread_mutex_t mutexa;
    pthread_mutex_t mutexb;
    
    void* another(void* arg){
        pthread_mutex_lock(&mutexb);
        printf("in child thread, got mutex b, waitiing for mutex a\n");
        sleep(5);
        ++b;
        pthread_mutex_lock(&mutexa);
        b += a++;
        pthread_mutex_unlock(&mutexa);
        pthread_mutex_unlock(&mutexb);
        pthread_exit(NULL);
    }
    
    int main ( int argc, char **argv)
    {
       pthread_t id;
       
       pthread_mutex_init(&mutex_a, NULL);
       pthread_mutex_init(&mutex_b, NULL);
       pthread_create( &id, NULL, another,NULL);
       
       pthread_mutex_lock(&mutexa);
       printf("in parent thread, got mutex a, waiting for b\n");
       sleep(5);
       ++a;
       pthread_mutex_lock(&mutexb);
       a += b++;
       pthread_mutex_unlock(&mutexb);
       pthread_mutex_unlock(&mutexa);
       
       pthread_join(id, NULL);
       pthread_mutex_destroy(&mutexa);
       pthread_mutex_destroy(&mutexb);
       return 0;
    }

    分析:

    • 主线程试图先占有互斥锁mutex_a,且又申请互斥锁mutex_b,并在两个互斥锁的保护下,操作变量a和b,最后才一起释放两个互斥锁;
    • 与此同时,子线程按照相反的顺序来申请互斥锁mutex_a和mutex_b,并在两个锁的保护下操作变量a和b。
    • 我们用sleep(5)为了模拟连续两次调用pthread_mutex_lock之间的时间差,以确保代码中的两个线程各自先占有一个互斥锁(主线程占有mutex_a,子线程占有mutex_b),然后等待另外一个互斥锁(主线程等待mutex_b,子线程等到mutex_a)
    • 两个线程将僵持住,谁都不能继续往下执行,从而形成死锁;
    • 如果代码中不加sleep函数,也许这段代码能够运行成功,但是会为程序留下一个潜在哦BUG.

    4.在多线程程序中调用fork导致死锁

    #include <pthread.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <wait.h>
    
    pthread_mutex_t mutex;
    
    void* another(void* arg){
        printf("in child thread, lock the mutex\n");
        pthread_mutex_lock(&mutex);
        sleep(5);
        pthread_mutex_unlock(&mutex);;
    }
    
    int main ( int argc, char **argv)
    {
       pthread_t id;
       
       pthread_mutex_init(&mutex, NULL);
       pthread_create( &id, NULL, another,NULL);
       /*父进程中的主线程暂停1s,以确保在执行fork操作之前,子线程已经开始运行并获得了互斥变量mutex*/
       sleep(1);
       int pid = fork();
       if( pid < 0 ){
           pthread_join( id, NULL );
           pthread_mutex_destroy( &mutex );
           return 1;
       }
       else if( pid == 0 ){
           printf("i am in the child,want to get the lock\n");
           /*子进程从父进程继承了互斥锁的状态,该互斥锁处于锁住的状态,这是由父进程中的子线程执行pthread_mutex_lock引起的,因此,下面这句加锁操作会一直阻塞,因为fork出来的子进程拷贝一份主进程的内存,但是主进程和子进程是两份不同的内存,即使主进程中的子线程释放了锁,也不会对子进程中的锁造成任何影响*/
           pthread_mutex_lock( &mutex );
           printf( "i can not run t here, opp..." );
           pthread_mutex_unlock( &mutex );
           exit(0);
       }
       else{
           wait(NULL);
       }
       pthread_join( id, NULL );
       pthread_mutex_destroy( &mutex );
       return 0;
    }
    

    分析:

    • 子进程只拥有一个执行线程,它是调用fork的那个线程的完整复制。
    • 子进程中将自动继承父进程中的互斥锁的状态,也就是说,父进程中已经被加锁的互斥锁在子进程中也是锁住的,这将引发下面问题:
    • 子进程可能不清楚从父进程继承而来的互斥锁的具体状态(是加锁状态还是解锁状态);这个互斥锁已经加锁了,但不是由调用fork函数的那个线程锁住的,而是由其他线程锁住的。由于主进程和子进程有两份不同的内存,即使主进程中的子线程释放掉锁,也不会影响子进程中的锁的状态,这种情况就是上面代码情况,导致死锁;
    • 针对上面代码情况死锁,pthread提供了一个专门函数pthread_atfork,以确保fork调用后父进程和子进程都拥有一个清楚的锁状态:int pthread_arfork(void (*prepare)(void), void (*parent)(void),void (*child)(void)) ;
    • 上面该函数将建立3个fork句柄来帮助我们清理互斥锁的状态。prepare句柄将在fork调用创建出子进程之前被执行,它用来锁住所有父进程中的互斥锁;parent句柄这是fork调用创建出子进程之后,而fork返回之前,在父进程中被执行,它的作用:释放所有在prepare句柄中被锁住的互斥锁;child句柄是fork返回之前,在子进程中被执行,和parent句柄一样,child句柄也是用于释放所有在prepare句柄中被锁住的互斥锁。该函数成功返回0,失败返回错误代码。
    • 通过将pthread_atfork代替fork避免死锁
    void prepare(){
        pthread_mutex_lock( &mutex );
    } 
    void infork(){
        pthread_mutex_unlock( &mutex );
    }
    pthread_atfork( prepare, infork, infork );





    展开全文
  • 主要介绍了举例讲解Python中的死锁、可重入锁和互斥锁,尽管线程编程方面Python的GIL问题老生常谈...需要的朋友可以参考下
  • 【Linux系统编程】互斥锁

    千次阅读 2019-11-07 21:21:37
    互斥锁相关函数3.1 互斥锁初始化3.2 互斥锁加锁3.3 互斥锁解锁3.4 互斥锁销毁04. 互斥锁示例05. 总结 01. 互斥锁引入 为什么需要互斥锁? 在多任务操作系统中,同时运行的多个任务可能都需要使用同一种资源。这个...
  • 互斥锁: mutex,用于保证在任何时刻,都只能一个线程访问该对象。 当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒。 读写锁: rwlock,分为读锁和写锁。处于读操作时,可以允许多个线程 同时获得...
  • 封装好了的互斥锁

    2017-06-15 11:05:50
    基于windows互斥锁,自己封装的类。使用起来非常方便,不需要手工lock,unlock什么的。只需要一对{},将代码直接加入空项目即可。
  • 互斥锁例程

    2015-09-22 14:40:31
    互斥锁例程

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 183,530
精华内容 73,412
关键字:

互斥锁有哪些