前情提要
FreeRTOS ~(四)同步互斥与通信 ~ (1/3)同步的缺陷
FreeRTOS ~(五)队列的常规使用 ~ (1/5)队列解决同步缺陷
举例子说明:利用信号量解决前述的"同步的缺陷"问题
使用信号量的时候记得包含相关头文件,及打开相关宏定义
#include "semphr.h"
#define configUSE_COUNTING_SEMAPHORES 1
这里使用的例子仍和开篇两个链接的一致
/*-----------------------------------------------------------*/
static int sum = 0; /* i自增,结果存放变量sum */
static volatile int flagCalcEnd = 0;/* 该Flag用于监测代码运行时长 */
static SemaphoreHandle_t xSemCalc; /* 创建的计数型信号量的句柄 */
/*-----------------------------------------------------------*/
void Task1Function(void * param)
{volatile int i = 0;while (1){for (i = 0; i < 10000000; i++)sum++;/* 上述计算完成,就将计数型信号量增加1 */xSemaphoreGive(xSemCalc);/* 当前代码是删除Task1,后面会讲到如果把这个删除了会怎么样?如何保证sum这个数值不被破坏? */vTaskDelete(NULL);}
}void Task2Function(void * param)
{while (1){/* flag用逻辑分析仪查看代码执行时长 */flagCalcEnd = 0;/* 阻塞时间设置为最大,要一直等待直到Task1计算完成,一旦能获取到信号量,就会退出阻塞态 */xSemaphoreTake(xSemCalc, portMAX_DELAY);flagCalcEnd = 1;printf("sum = %d\r\n", sum);}
}
/*-----------------------------------------------------------*/
int main( void )
{prvSetupHardware();/* 创建计数型信号量,最大计数值设置为10,初始计数值设置为0在本例程设计中Task1完成复杂的计算后,就give一次,也就是计数值会+1;只是验证信号量解决同步问题,这里设置为1也能满足需求,暂且定为10次,后续可以让Task1 give多次 */xSemCalc = xSemaphoreCreateCounting(10, 0);xTaskCreate(Task1Function, "Task1", 100, NULL, 1, NULL);xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);vTaskStartScheduler();return 0;
}
Debug执行代码,用逻辑分析仪抓取flag变化,依此监测代码运行时长;
下图可以看到的是,代码只执行了3s就完成了计算,也就是在Task1进行计算的时候,Task2进入了阻塞态,没有参与调度,
因此3s可以认为是Task1独享CPU资源后,Task1完成计算的时间.
这就是我们想要的结果,解决了同步的缺陷.
上文中有提到,Task1执行完复杂的计算后就 vTaskDelete(NULL); 将自己删除掉了.
如果这里不删除掉会怎么样呢?
将上述代码中,仅修改这一个位置,修改如下:
void Task1Function(void * param)
{volatile int i = 0;while (1){for (i = 0; i < 10000000; i++)sum++;xSemaphoreGive(xSemCalc);//vTaskDelete(NULL);}
}
运行观察会出现什么问题?
下图说明:Task1的计算完成了,并且将计数型信号量加1了,但是打印的数据出现了问题;
也就是说没有计数型信号量做不到像队列那样可以将数据保存下来不被更改;
Task1计算完成for循环,give之后,此时如果Tick中断没有到呢?
那么Task1将继续执行for循环,sum将继续自增,因此我们要加上一些代码,实现想要的结果.
在上述代码的基础上,加入队列,以保证数据不被更改.
/*-----------------------------------------------------------*/
static int sum = 0; /* i自增,结果存放变量sum */
static volatile int flagCalcEnd = 0;/* 该Flag用于监测代码运行时长 */
static SemaphoreHandle_t xSemCalc; /* 创建的计数型信号量的句柄 */
static QueueHandle_t xQueueHandle1; /* 创建的队列的句柄 */
/*-----------------------------------------------------------*/
void Task1Function(void * param)
{volatile int i = 0;while (1){for (i = 0; i < 10000000; i++)sum++;xSemaphoreGive(xSemCalc);/* 将计算完成的数据加入到队列中 */xQueueSend(xQueueHandle1,&sum,portMAX_DELAY);/* 这里同时将sum清零,验证上述的问题,这一步可做可不做 */sum = 0;}
}void Task2Function(void * param)
{int val = 0;while (1){flagCalcEnd = 0;xSemaphoreTake(xSemCalc, portMAX_DELAY);flagCalcEnd = 1;/* 队列接收数据,这里阻塞时间为0,因为代码能够执行到这里,说明队列中一定是有数据的,因此无需等待 */xQueueReceive(xQueueHandle1,&val,0);printf("sum = %d\r\n", val);}
}
/*-----------------------------------------------------------*/
int main( void )
{prvSetupHardware();/* 创建队列 */xQueueHandle1 = xQueueCreate(2,sizeof(int));if( xQueueHandle1 == NULL ){printf("can not create queue 1\r\n");}/* 创建计数型信号量 */xSemCalc = xSemaphoreCreateCounting(10, 0);xTaskCreate(Task1Function, "Task1", 100, NULL, 1, NULL);xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);vTaskStartScheduler();return 0;
}
执行结果如下: