举例子说明:互斥的缺陷
验证互斥:
创建两个任务,Task3和Task4,用来打印字符串;
我们创建一个新的函数,通用函数,被Task3和Task4使用
void TaskGenericFunction(void * param)
{
while (1)
{
printf("%s\r\n", (char *)param);
}
}
int main( void )
{
TaskHandle_t xHandleTask1;
/* 硬件初始化 */
prvSetupHardware();
//xTaskCreate(Task1Function,"Task1",100,NULL,1,&xHandleTask1);
//xTaskCreate(Task2Function,"Task2",100,NULL,1,NULL);
xTaskCreate(TaskGenericFunction, "Task3", 100, "Task 3 is running", 1, NULL);
xTaskCreate(TaskGenericFunction, "Task4", 100, "Task 4 is running", 1, NULL);
/* Start the scheduler. */
vTaskStartScheduler();
return 0;
}
我们期望得到的是打印顺序是:
...
Task 3 is running
Task 4 is running
Task 3 is running
Task 4 is running
Task 3 is running
...
但是我们这样写代码是否可以做到呢?
分析一下;
为什么会造成上述打印乱的现象呢?
由于本次打印的字符较多,如:Task 3 is running,想要打印完这几个字符所需的耗时超出了1ms任务调度周期,
比如当前Running任务是Task3,可能才刚打印了Task 3 i 这几个字符,就被Tick中断打断,
任务调度切换到Task4去了,因此造成上图中的现象。
这里面如果波特率提高,可能会出现打印N次完整的Task 3 is running,但是总有新的一行打印会被Tick中断掉
这就是互斥的缺陷,并且下面可以对互斥进一步说明,看似没有问题的代码,就真的没有问题吗?
看下面的代码示例:
void TaskGenericFunction(void * param)
{
while (1)
{
if (!flagUARTused)
{
flagUARTused = 1;
printf("%s\r\n", (char *)param);
flagUARTused = 0;
vTaskDelay(1);
}
}
}
上述代码通过加入一个标志位卡控一下,但是要注意必须加上延时;
可以使用vTaskDelay(1);也可以使用vTaskDelayUntil(a,b);
原因是,如:这里是先创建的Task3,后创建的Task4且两者优先级均大于0且一致,那么先执行的就是Task4;
然后由于这里标志位的卡控,导致Task3可能无法打印成功;
然后一切就绪,Debug可以看到串口一直按照想要的结果进行输出,也就是:
...
Task 3 is running
Task 4 is running
Task 3 is running
Task 4 is running
Task 3 is running
...
但是,这样的代码真的没有问题吗?请看下面的分析:
如果Tick中断在上图中的红色箭头所示的地方发生了切换,该怎么办?
Task3和Task4将出现一次错误;
这里仅是一个示例,实际工程代码往往不会这么简单,上述的代码在执行了几天几夜后,总是有出现这样错误的时候;
而FreeRTOS是要解决更多问题的,因此针对这样问题,必然是有相应的手段的。