首页 > 解决方案 > 互斥锁如何保证 freeRTOS 中的所有权?

问题描述

我正在使用 esp32 在 freeRTOS 中使用 Mutex。在一些文档中,我已经阅读了互斥锁保证所有权,这意味着如果一个线程(我们将其命名为 task_A)锁定了一个关键资源(获取令牌),其他线程(task_B 和 task_C)将保持在等待模式下等待该资源被解锁由锁定它的同一线程(即task_A)。我试图通过设置其他任务(task_B 和 task_C)来证明这一点,以便在开始做任何事情之前给出一个令牌,然后它会尝试从互斥锁持有者那里获取一个令牌,这令人惊讶地工作而没有显示任何错误的孩子. 好吧,我用来验证或显示事情如何工作的方法我创建了一个显示函数,该函数读取每个任务发布(设置和清除)的事件(当它处于等待模式时,它会设置等待位,如果它' s 工作它会设置工作位等等......,你明白了)。以及一个简单的 printf() 以防获取或提供函数(xSemaphoreTake != true 和 xSemaphoreGive != true)出错。

我不能使用调试模式,因为我没有任何类型的微控制器调试器。

这是我正在尝试做的一个示例:我创建了许多任务,每个任务都会调用此函数,但在不同的时间使用不同的设置。

void vVirtualResource(int taskId, int runTime_ms){
  int delay_tick = 10;
  int currentTime_tick = 0;
  int stopTime_tick = runTime_ms/portTICK_PERIOD_MS;
  if(xSemaphoreGive(xMutex)!=true){
      printf("Something wrong in giving first mutex's token in task id: %d\n", taskId);
  }

  while(xSemaphoreTake(xMutex, 10000/portTICK_PERIOD_MS) != true){
    vTaskDelay(1000/portTICK_PERIOD_MS);
  }
// notify that the task with <<task id>> is currently running and using this resource
  switch (taskId)
  {
  case 1:
    xEventGroupClearBits(xMutexEvent, EVENTMASK_MUTEXTSK1);
    xEventGroupSetBits(xMutexEvent, EVENTRUN_MUTEXTSK1);
    break;
  case 2:
    xEventGroupClearBits(xMutexEvent, EVENTMASK_MUTEXTSK2);
    xEventGroupSetBits(xMutexEvent, EVENTRUN_MUTEXTSK2);
    break;
  case 3:
    xEventGroupClearBits(xMutexEvent, EVENTMASK_MUTEXTSK3);
    xEventGroupSetBits(xMutexEvent, EVENTRUN_MUTEXTSK3);
    break;
  default:
    break;
  }
  // start running the resource
  while(currentTime_tick<stopTime_tick){
    vTaskDelay(delay_tick);
    currentTime_tick += delay_tick;
  }
  // gives back the token
  if(xSemaphoreGive(xMutex)!=true){
    printf("Something wrong in giving mutex's token in task id: %d\n", taskId);
  }

}

您会注意到第一次,将开始在处理器中运行的第一个任务将打印出第一条错误消息,因为当互斥锁持有者中仍有令牌时它无法提供令牌,这是正常的,所以我忽略它。

希望有人可以向我解释互斥锁如何使用 freeRTOS 中的代码保证所有权。首先,我没有使用第一个 xSemaphoreGive 函数,它运行良好。但这并不意味着它可以保证任何事情。或者我编码不正确。

谢谢你。

标签: mutexfreertosesp32

解决方案


您的示例非常复杂,我也没有看到明确的代码task_Atask_B或者task_C我将尝试解释一个更简单的示例,希望能解释互斥锁如何保证资源所有权。

使用互斥锁的一般方法如下:

void doWork()
{
  // attempt to take mutex
  if(xSemaphoreTake(mutex, WAIT_TIME) == pdTRUE)
  {
    // mutex taken - do work
    ...
    // release mutex
    xSemaphoreGive(mutex);
  }
  else
  {
    // failed to take mutex for 'WAIT_TIME' amount of time
  }
}

上面的doWork函数是可能被多个线程同时调用,需要保护的函数。对于需要保护的给定资源上的每个功能,此模式重复。如果资源更复杂,一个好方法是保护线程可调用的最顶层函数,然后如果成功采用互斥锁,则调用执行实际工作的内部函数。

您所说的所有权保证是声明下可能不会有多个上下文(线程,还有中断)if(xSemaphoreTake(mutex, WAIT_TIME) == pdTRUE)。换句话说,如果一个上下文成功地获取了互斥锁,则保证没有其他上下文也能够获取它,除非原始上下文xSemaphoreGive首先释放它。

现在至于您的场景-虽然我并不完全清楚它应该如何工作,但我可以看到您的代码有两个问题:

  1. xSemaphoreGive在函数的开头 - 不要那样做。互斥锁默认是“给予”的,如果你不是第一个“接受”它的人,你就不应该“给予”它。总是把一个xSemaphoreGive下一个成功xSemaphoreTake而无处可去。

  2. 此代码块:

    while(xSemaphoreTake(xMutex, 10000/portTICK_PERIOD_MS) != true){
      vTaskDelay(1000/portTICK_PERIOD_MS);
    }
    

    如果您需要等待互斥锁更长的时间 - 指定更长的时间。如果您想要无限等待,只需指定最长可能时间 ( 0xFFFFFFFF)。在您的场景中,您每 10 秒轮询一次互斥锁,然后延迟 1 秒,在此期间互斥锁未实际检查,这意味着在其他线程释放互斥锁后,您将不得不等待近一秒钟开始在请求它的当前线程中工作。等待互斥锁已经由 RTOS 以最佳方式完成 - 一旦它被释放,它会唤醒当前等待互斥锁的最高优先级任务,没有必要做更多的事情。

如果我要就如何修复您的示例提供建议 - 简化它并且不要做超出需要的操作,例如额外调用xSemaphoreGive或实现您自己的等待互斥锁。将执行某些工作的代码部分隔离到一个单独的函数中,该函数xSemaphoreTake在最顶部执行一次调用,并且xSemaphoreGive仅在xSemaphoreTake成功时才进行一次调用。然后从不同的线程调用这个函数来测试它是否有效。


推荐阅读