第10个程序
1.同步与互斥的概念
临界资源:同一时间只能有一个人使用的资源:
比如串口打印:如果两个任务同时使用串口打印,就会导致打印出来的数据非常混乱,必须需要两个人交叉使用,等A打印完了,B才能使用串口开始打印。那么A和B就需要互斥的访问这个资源。同步的话就是,比如A在用串口打印,那么B必须等待,B等待A做完这件事就是同步
2.同步的例子:有缺陷
实现以下功能:
我们让任务1,执行一个简单的操作,然后任务2需要等待任务1完成这件事,
这里用到 volatile关键字:
-
volatile :不稳定的,用它定义的变量,也就是不稳定的,意思是说,这个变量可能会意想不到的改变
-
他就是告诉编译器,不要优化这个变量代码,因为这个变量可能会意外的改变,每次用到这个变量的时候,都需要重新去内存中读取这个变量,即使刚刚已经用过了这个变量。
static int sum = 0; static int flagcalend = 0; void TaskFunction_1( void * param ) { volatile int i = 0; while(1) { for(i=0;i<10000000;i++) { sum++; } flagcalend=1; printf("1"); vTaskDelete(NULL); } } void TaskFunction_2( void * param ) { while(1) { if(flagcalend==1) { flagcalend=0; printf("%d\r\n",sum); } } } int main( void ) { TaskHandle_t xHandleTask1;//任务1的句柄 #ifdef DEBUG debug(); #endif prvSetupHardware(); printf("hello,world\r\n"); xTaskCreate(TaskFunction_1,"task1",100,NULL,1,&xHandleTask1); xTaskCreate(TaskFunction_2,"task2",100,NULL,1,NULL); /* Start the scheduler. */ vTaskStartScheduler(); /* Will only get here if there was not enough heap space to create the idle task. */ return 0; }
通过这个可以看出,刚开始1任务一直在运行,然后任务2等待他运行,此时任务1运行时间为4s左右
那么如果我们把任务2删除,只有任务1一个任务

可以看到如果只运行任务1,其实只需要2s左右,也就是在同步的时候,两个任务共同抢占cpu的资源,导致运行效率比较低,那么在任务1运行的时候,同步的任务应该需要进入blocked或者休眠状态,不能处于ready状态,这样不浪费cpu资源
3.互斥的例子:有缺陷
我们写一个通用函数,然后创建两个任务4和5,分别打印task4 is running和task 5 is running,假如没有互斥:
static int sum = 0;
static int flagcalend = 0;
void TaskFunction_1(void *param)
{
volatile int i = 0;
while (1)
{
for (i = 0; i < 10000000; i++)
{
sum++;
}
flagcalend = 1;
printf("1");
vTaskDelete(NULL);
}
}
void TaskFunction_2(void *param)
{
while (1)
{
if (flagcalend == 1)
{
printf("%d\r\n", sum);
}
}
}
void TaskGenricFunction(void *param)
{
while (1)
{
printf("%s\r\n", (char *)param);
}
}
int main(void)
{
TaskHandle_t xHandleTask1; // 任务1的句柄
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("hello,world\r\n");
xTaskCreate(TaskFunction_1, "task1", 100, NULL, 1, &xHandleTask1);
// xTaskCreate(TaskFunction_2,"task2",100,NULL,1,NULL);
xTaskCreate(TaskGenricFunction, "task4", 100, "task4 is running", 1, NULL);
xTaskCreate(TaskGenricFunction, "task5", 100, "task5 is running", 1, NULL);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}

可以看到两个任务争相抢用串口,每个任务都没办法完整的打印,因此我们需要互斥,一个任务打印的时候,另一个任务不能用串口,加上一个标志位
static int flaguartuesd = 0;
void TaskGenricFunction(void *param)
{
while (1)
{
if (flaguartuesd == 0)
{
flaguartuesd = 1;
printf("%s\r\n", (char *)param);
flaguartuesd = 0;
}
}
}

此时可以看到,只有任务5在执行,任务4并没有执行,好像并没有时间片论战,其实这是因为没有延迟的原因:
如果没有delay的话,因为这个任务它运行一次的时间和时间片切换的那个间隔是不一样的,假如说:这个任务执行一次需要1.2ms,然后每1ms发生一次中断轮换,刚开始是任务3,他打印,同时将flag设为1,但是并不能执行完,然后就要切换,切换到任务4,但是这个时候flag是1,那么他打印不了所以就啥也干不了,再等1ms,切换回任务3继续打印,然后再花费0.2ms打印,但是这个时候flag要置为0,而并没有到切换的时间,那么还是执行任务3,这个时候,flag又要被置为1,然后如果再到任务4,还是执行了因为flag为1.大概就是这样,不过其实还有一些其他可能,比如这个任务执行一次需要1.5ms,那么执行两次之后刚好到切换的时刻,这种就属于临界状态了,不好讨论,但是按照这个实验现象来说,可以按照我说的来理解一下。
那么我们加上延迟:
void TaskGenricFunction(void *param)
{
while (1)
{
if (flaguartuesd == 0)
{
flaguartuesd = 1;//这一行
printf("%s\r\n", (char *)param);
flaguartuesd = 0;
vTaskDelay(1);
}
}
}

可以看到这个时候的现象是正常的,任务4,任务5,互斥的使用串口。这时候看起来好像就没问题了,但是其实还是有隐患的,如果运行时间长的话,可能会出现一种特殊情况:
比如说:任务4(或者5)执行到了我上面标注的那一行的上面,还没来得及赋值就切换,此时该任务5执行了,那么他就正常执行打印操作,然后比如没打印完,就到时间,然后切换回来,那么对于任务4,他是要接着向下执行,所以任务4他也需要串口打印,这个时候其实就是出现没加互斥的现象。那么就会有概率出现这种问题,这个问题关键在于,判断和设置之间执行的时间不够快。
4.通信的例子:有缺陷
可以使用全局变量来通信,比如前面所说的sum,可以在任务1和任务2中互相调用
5.FreeRTOS的解决方案
-
正确性(互斥的缺陷)
-
效率(同步的缺陷)
-
多种解决方案:队列(queue,FIFO),事件组,信号量,任务通知,互斥量等等,后面再具体学习。
522

被折叠的 条评论
为什么被折叠?



