文章目录
- 一、同步互斥改进方法
- 二、队列实现同步
- 三、队列实现互斥
- 总结
一、同步互斥改进方法
在上一章同步互斥中,我们有两个实验。
同步实验: 我们创建了两个任务,
任务1
循环遍历一个比较大的数字,遍历完成后设置标志位置1。任务2
等待任务1
标志位置1后串口发送数据,但是在任务2
等待过程中也参与CPU的调度,从而导致任务1计算时间增加。
既然任务2
在等待,那如何配置才能让任务2
不抢占CPU资源。
互斥实验: 两个任务交替使用一个函数发送串口数据。
原同步Demo
改进方案: 让任务1
计算完数值后把数字写入队列,任务2
去读取队列,当队列中没有数据任务2
是阻塞状态,当队列中有数据他就打印出来。
void Task1Function(void * param)
{
volatile int i = 0;
while (1)
{
for (i = 0; i < 10000000; i++)
sum++;
//printf("1");
flagCalcEnd = 1;
vTaskDelete(NULL);
}
}
void Task2Function(void * param)
{
while (1)
{
if (flagCalcEnd)
printf("sum = %d\r\n", sum);
}
}
原互斥Demo
改进方案: 通过读队列和写队列 来获得串口的使用权 和 释放使用权。
void TaskGenericFunction(void * param)
{
while (1)
{
printf("%s\r\n", (char *)param);
}
}
}
//任务调用伪代码
xTaskCreate(TaskGenericFunction, "Task3", 100, "Task 3 is running", 1, NULL);
xTaskCreate(TaskGenericFunction, "Task4", 100, "Task 4 is running", 1, NULL);
二、队列实现同步
//创建句柄
static QueueHandle_t xQueueCalcHandle;
创建队列
xQueueCalcHandle = xQueueCreate(2, sizeof(int));//队列长度为2 每个数据sizeof(int)字节长度
if (xQueueCalcHandle == NULL)//返回值非0返回句柄,返回NULL代表识别
{
printf("can not create queue\r\n");
}
任务1
往队列发送sum,拷贝地址数值到队列,所以在后面修改sum是不会影响队列中的sum数值。任务2
读队列,读到数据存入val。
void Task1Function(void * param)
{
volatile int i = 0;
while (1)
{
for (i = 0; i < 10000000; i++)
sum++;
//printf("1");
//flagCalcEnd = 1;
//vTaskDelete(NULL);
xQueueSend(xQueueCalcHandle, &sum, portMAX_DELAY);
sum = 1;
}
}
void Task2Function(void * param)
{
int val;
while (1)
{
//if (flagCalcEnd)
flagCalcEnd = 0;
xQueueReceive(xQueueCalcHandle, &val, portMAX_DELAY);
flagCalcEnd = 1;
printf("sum = %d\r\n", val);
}
}
结果:在2s时间flag置1,我们使用队列实现了同步。
三、队列实现互斥
//创建句柄
static QueueHandle_t xQueueUARTcHandle;
初始化串口,创建队列
int InitUARTLock(void)
{
int val;
xQueueUARTcHandle = xQueueCreate(1, sizeof(int));
if (xQueueUARTcHandle == NULL)
{
printf("can not create queue\r\n");
return -1;
}
xQueueSend(xQueueUARTcHandle, &val, portMAX_DELAY);//队列里面有数据,表示别人可以读取数据
return 0;
}
读队列和写队列
void GetUARTLock(void)
{
int val;
xQueueReceive(xQueueUARTcHandle, &val, portMAX_DELAY);//去读队列,得到数据就说明得到串口的使用权
}
void PutUARTLock(void)//释放
{
int val;
xQueueSend(xQueueUARTcHandle, &val, portMAX_DELAY);//用完串口就往队列写一个数据,这说明用完串口了,把使用权释放掉
}
串口任务,这里加
vTaskDelay(1);
是因为当task3
串口发送完成 写队列后,task4
处于阻塞状态而task3
是运行状态,这时task3
能立刻读队列再次得到串口的使用权,所以要加一个阻塞状态给写队列的任务。
void TaskGenericFunction(void * param)
{
while (1)
{
GetUARTLock();
printf("%s\r\n", (char *)param);
// task 3 is waiting
PutUARTLock(); /* task 3 ==> ready, task 4 is running */
vTaskDelay(1);
}
}
任务创建
xTaskCreate(TaskGenericFunction, "Task3", 100, "Task 3 is running", 1, NULL);
xTaskCreate(TaskGenericFunction, "Task4", 100, "Task 4 is running", 1, NULL);
结果:
任务3
和任务4
交替写队列和读队列,实现了任务交替执行
总结
队列使用主要就是 创建队列、写队列、读队列。
传输的数据可以是地址 也可以是数据。