C++互斥锁和RTOS xMutex之间的区别

Difference between C++ mutex and RTOS xMutex

本文关键字:xMutex 之间 区别 RTOS C++      更新时间:2023-10-16

我正在尝试锁定 ESP32。显然,有不同的方法可以实现锁:

  1. 有默认的C++互斥库:

    #include <mutex>
    std::mutex mtx;
    mtx.lock();
    mtx.unlock();
    
  2. 还有RTOS的实现:

    SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();
    xSemaphoreTake(xMutex, portMAX_DELAY);
    xSemaphoreGive(xMutex);
    

我应该注意哪些根本差异? 还是等价的?

假设您使用的是 ESP-IDF SDK,工具链基于 GCC 5.2,面向 xtensa-lx106 指令集,具有部分开源的 C 运行时库。

在 GNU libstdc++ 中std::mutex委托pthread_mutex_lock/解锁调用。ESP-IDF SDK 包含一个 pthread 仿真层,我们可以看到pthread_mutex_lockpthread_mutex_unlock实际做了什么:

static int IRAM_ATTR pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickType_t tmo)
{
if (!mux) {
return EINVAL;
}
if ((mux->type == PTHREAD_MUTEX_ERRORCHECK) &&
(xSemaphoreGetMutexHolder(mux->sem) == xTaskGetCurrentTaskHandle())) {
return EDEADLK;
}
if (mux->type == PTHREAD_MUTEX_RECURSIVE) {
if (xSemaphoreTakeRecursive(mux->sem, tmo) != pdTRUE) {
return EBUSY;
}
} else {
if (xSemaphoreTake(mux->sem, tmo) != pdTRUE) {
return EBUSY;
}
}
return 0;
}
int IRAM_ATTR pthread_mutex_unlock(pthread_mutex_t *mutex)
{
esp_pthread_mutex_t *mux;
if (!mutex) {
return EINVAL;
}
mux = (esp_pthread_mutex_t *)*mutex;
if (!mux) {
return EINVAL;
}
if (((mux->type == PTHREAD_MUTEX_RECURSIVE) ||
(mux->type == PTHREAD_MUTEX_ERRORCHECK)) &&
(xSemaphoreGetMutexHolder(mux->sem) != xTaskGetCurrentTaskHandle())) {
return EPERM;
}
int ret;
if (mux->type == PTHREAD_MUTEX_RECURSIVE) {
ret = xSemaphoreGiveRecursive(mux->sem);
} else {
ret = xSemaphoreGive(mux->sem);
}
if (ret != pdTRUE) {
assert(false && "Failed to unlock mutex!");
}
return 0;
}

如您所见,它主要将调用委托给 RTOS 信号量 API,并进行一些额外的检查。

您可能不需要/想要这些支票。鉴于 esp32 芯片的微小 i-cache 和极其缓慢的串行 RAM,我宁愿尽可能靠近硬件(即不要使用std::mutex,除非它完全符合您的需求(。

我应该注意哪些根本差异?

我不熟悉您在第二个示例中调用的 API,但看起来您的xMutex变量引用了计数信号量。"信号量"抽象比"互斥"抽象更强大。也就是说,您始终可以使用信号量作为互斥锁的替代品,但在某些算法中,互斥锁不能替代信号量。

我喜欢将信号量视为无信息令牌的阻塞队列。 "give"操作将一个令牌放入队列中,而"take"从队列中获取一个令牌,如果队列恰好在调用take((时为空,则可能会等待其他线程提供令牌。

P.S.,为了使用信号量作为互斥锁的替代品,当互斥锁应该"自由"时,你需要它包含一个令牌,当互斥锁应该"正在使用"时,你需要

它包含一个令牌,当互斥锁应该"使用中"时,它需要包含零个令牌。 这意味着,您需要创建信号量的代码确保它在开始时包含一个令牌

示例中的xMutex = xSemaphoreCreateMutex()语句未显式显示新信号灯包含的标记数。 如果是零个标记,那么您可能希望下一行代码"给(("一个标记以完成初始化。