C++ mutex 和 RTOS xMutex 的区别

Difference between C++ mutex and RTOS xMutex

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

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

    #include <mutex>
    
    std::mutex mtx;
    
    mtx.lock();
    
    mtx.unlock();
    
  2. 还有 implementation from RTOS:

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

我应该注意哪些根本差异? 或者它们是等价的?

Are there fundamental differences I should be aware of?

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

我喜欢将信号量视为无信息标记的 blocking queue。 "give" 操作将一个令牌放入队列,而 "take" 从队列中取出一个令牌,如果队列恰好在 take( ) 被调用。


P.S., 为了使用信号量代替互斥锁,当互斥锁应为 "free" 时,您需要它包含一个标记,而当mutex 应该是 "in use." 这意味着,您需要创建信号量的代码确保它在开始时包含一个标记

您示例中的 xMutex = xSemaphoreCreateMutex() 语句没有明确显示新信号量包含多少标记。如果它是零个标记,那么您可能希望下一行代码 "give()" 一个标记以完成初始化。

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

GNU libstdc++ 中的

std::mutex 委托给 pthread_mutex_lock/unlock 调用。 ESP-IDF SDK 包含一个 pthread emulation layer, where we can see what pthread_mutex_lock and pthread_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,并进行一些额外的检查。

您很可能没有 need/want 这些支票。考虑到 esp32 芯片的微小 i-cache 和极慢的串行 RAM,我宁愿尽可能靠近硬件(即不要使用 std::mutex 除非它完全满足你的需要)。