RTX线程通信之——互斥锁

Posted 玖道

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RTX线程通信之——互斥锁相关的知识,希望对你有一定的参考价值。

文章目录

Mutex

互斥锁(Mutex)是线程间实现资源同步的重要机制之一。

概念

Mutex stands for “Mutual Exclusion”. In reality, a mutex is a specialized version of semaphore. Mutual exclusion (widely known as Mutex) is used in various operating systems for resource management. Many resources in a microcontroller device can be used repeatedly, but only by one thread at a time (for example communication channels, memory, and files). Mutexes are used to protect access to a shared resource. A mutex is created and then passed between the threads (they can acquire and release the mutex).

抓重点,互斥锁的特点如下:

  • 互斥锁是一种特殊的信号量。
  • 互斥锁被用于资源管理,管理那种一次只允许一个线程访问的共享资源,如通信通道,内存、文件等
  • 互斥锁被用于资源管理的本质就是保护共享资源。
  • 互斥锁被创建后,在线程之间传递;线程可以获取或者释放互斥锁

A mutex is a special version of a semaphore. Like the semaphore, it is a container for tokens. But instead of being able to have multiple tokens, a mutex can only carry one (representing the resource). Thus, a mutex token is binary and bounded, i.e. it is either available, or blocked by a owning thread. The advantage of a mutex is that it introduces thread ownership. When a thread acquires a mutex and becomes its owner, subsequent mutex acquires from that thread will succeed immediately without any latency (if osMutexRecursive is specified). Thus, mutex acquires/releases can be nested.

这里谈到,互斥锁是一种特殊的信号量。什么是信号量?参考我的另一篇博客。互斥锁本质上是一个信号量,所以它就拥有信号量的特点:

  • 互斥锁是容器,不过只能拥有一个token,即管理一种资源。
  • 互斥锁相当于一个二进制位,只有两种状态,availableblocked
  • 互斥锁引入了线程所有权的概念,如果一个线程拥有了该互斥锁(且是递归锁),那么该线程下次获取该互斥锁时将不会有任何延迟。直到该线程释放这个互斥锁,否则别的线程无法拥有该互斥锁。

RTX互斥锁API

类型

  • osMutexAttr_t : 互斥锁属性结构体
  • osMutexId_t : 互斥锁ID

RTX提供了三种特殊的互斥锁:递归锁,优先级继承锁,鲁棒锁

【递归锁】

#define osMutexRecursive      0x00000001U    // 递归锁,自己可以循环使用

The same thread can consume a mutex multiple times without locking itself. Each time the owning thread acquires the mutex the lock count is incremented. The mutex must be released multiple times as well until the lock count reaches zero. At reaching zero the mutex is actually released and can be acquired by other threads.

// 官方案例
#include "cmsis_os2.h"
osMutexId_t mutex_id; 
const osMutexAttr_t Thread_Mutex_attr = 
  "myThreadMutex",     // human readable mutex name
  osMutexRecursive,    // attr_bits
  NULL,                // memory for control block   
  0U                   // size for control block
;
 
// must be called from a thread context
void UseMutexRecursively(int count) 
  osStatus_t result = osMutexAcquire(mutex_id, osWaitForever);  // lock count is incremented, might fail when lock count is depleted
  if (result == osOK) 
    if (count < 10) 
      UseMutexRecursively(count + 1);
    
    osMutexRelease(mutex_id); // lock count is decremented, actually releases the mutex on lock count zero
  

【优先级继承锁】

#define osMutexPrioInherit    0x00000002U    // 继承锁  继承等待线程中更高的优先级

A mutex using priority inheritance protocol transfers a waiting threads priority to the current mutex owner if the owners thread priority is lower. This assures that a low priority thread does not block a high priority thread.

优先级继承锁使得拥有该锁的线程可以继承等待线程中优先级比它高的线程的优先级(中文好绕~),这样做目的是防止低优先级的线程锁住高优先级的线程,避免了优先级反转现象。

// 官方案例
#include "cmsis_os2.h"
 
osMutexId_t mutex_id;  
 
const osMutexAttr_t Thread_Mutex_attr = 
  "myThreadMutex",     // human readable mutex name
  osMutexPrioInherit,  // attr_bits
  NULL,                // memory for control block   
  0U                   // size for control block
;
 
void HighPrioThread(void *argument) 
  osDelay(1000U); // wait 1s until start actual work
  while(1) 
    osMutexAcquire(mutex_id, osWaitForever); // try to acquire mutex
    // do stuff
    osMutexRelease(mutex_id);
  

 
void MidPrioThread(void *argument) 
  osDelay(1000U); // wait 1s until start actual work
  while(1) 
    // do non blocking stuff
  

 
void LowPrioThread(void *argument) 
  while(1) 
    osMutexAcquire(mutex_id, osWaitForever);
    osDelay(5000U); // block mutex for 5s
    osMutexRelease(mutex_id);
    osDelay(5000U); // sleep for 5s
  

【鲁棒锁】

#define osMutexRobust         0x00000008U    // 鲁棒锁  线程结束,则自动释放互斥锁

Robust mutexes are automatically released if the owning thread is terminated (either by osThreadExit or osThreadTerminate). Non-robust mutexes are not released and the user must assure mutex release manually.

如果一个线程拥有鲁棒锁,那么该线程结束后,会自动释放锁,避免了资源不可访问现象发生。

// 官方案例
#include "cmsis_os2.h"
 
osMutexId_t mutex_id;
 
const osMutexAttr_t Thread_Mutex_attr = 
  "myThreadMutex",     // human readable mutex name
  osMutexRobust,       // attr_bits
  NULL,                // memory for control block   
  0U                   // size for control block
;
 
void Thread(void *argument) 
  osMutexAcquire(mutex_id, osWaitForever);
  osThreadExit();

函数

osMutexNew

osMutexId_t osMutexNew(const osMutexAttr_t* attr)
// 输入
* 互斥锁属性结构体指针,默认为NULL
// 输出
* 互斥锁ID

osMutexGetName

const char* osMutexGetName(osMutexId_t mutex_id)
// 输入
* mutex_id : 互斥锁ID
// 输出
* 互斥锁名字

osMutexAcquire

osStatus_t osMutexAcquire(osMutexId_t mutex_id, uint32_t timeout)
// 输入
* mutex_id : 互斥锁ID
* timeout : 超时设定
// 输出
* 函数执行状态码:osOK、osErrorTimeout、osErrorResource、osErrorParameter、osErrorISR

osMutexRelease

osStatus_t osMutexRelease(osMutexId_t mutex_id)
// 输入
* mutex_id : 互斥锁ID
// 输出
* 函数执行状态码:osOK、osErrorResource、osErrorParameter、osErrorISR

osMutexGetOwner

osThreadId_t osMutexGetOwner(osMutexId_t mutex_id)
// 输入
* mutex_id: 互斥锁ID
// 输出
* 线程ID

osMutexDelete

osStatus_t osMutexDelete(osMutexId_t mutex_id)
// 输入
* mutex_id : 互斥锁ID
// 输出
* 函数执行状态码: osOK、osErrorResource、osErrorParameter、osErrorISR

互斥锁使用步骤

1.声明互斥锁属性结构体

static const osMutexAttr_t mutex_attr = 
   .name = "mutex_1",
;

属性结构体中可以声明为三种特殊锁之一。

2.创建互斥锁句柄

osMutexId_t mutex_id;

3.创建互斥锁

mutex_id = osMutexNew(&mutex_attr);

4.释放互斥锁

osMutexRelease(mutex_id)

其实,在前面的官方案例中,使用步骤已经很明确地给出啦~

小结

本文简单介绍了RTX中互斥锁这一资源管理对象,重点如下:

  • 互斥锁的概念
  • 互斥锁的实现原理,三种特殊锁,函数接口等
  • 互斥锁的使用步骤

参考资料

☞官方资料:互斥锁

以上是关于RTX线程通信之——互斥锁的主要内容,如果未能解决你的问题,请参考以下文章

Python从入门到精通(二十一)Python并发编程互斥锁的运用以及线程通信

RTX线程通信之——信号量

RTX线程通信之——信号量

RTX线程通信之——信号量

四十Linux 线程——线程同步之条件变量

Go并发编程之传统同步—互斥锁