生活中排队的现象天天都能看见,比如你去海底捞吃饭,高峰期就需要排队,这里排队要解决的问题就是商家由于座位有限,无法同时满足所有消费者的用餐需求,所以就需要排队,依次服务。
而我们FreeRTOS中要使用的队列,本质上和上面的情况也是一样的,对于队列,首先介绍几个概念:
队列:存储数据的一个缓冲区域,类似海底捞排位等号区的坐席位数,如果坐席满了,就不再接待排位了;
生产者:往队列里面发送数据的一方称之为生产者,类似海底捞的门迎服务台;
消费者:从队列里面取数据并处理的一方称之为消费者,类似海底捞的用餐引导员

对于ESP32中的队列,其主要用途有以下一些场景:
多任务之间消息通信:A任务处理的一些数据,需要同步给B任务,就可以通过队列实现,A作为生产者往队列里面发送消息,B作为消费者从队列里面取消息,就实现了A与B的消息通信。
削峰填谷:当短时间内需要处理的消息太多,并不能立马全部处理时,可以将消息暂存到队列,慢慢消费处理,起到一个消峰的效果。
创建队列
QueueHandle_t xQueueCreate( const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize)该函数用于创建一个队列,一共有两个参数:
uxQueueLength:这个队列的最大长度;
uxItemSize:队列中每一个元素所占的内存空间;
返回值为创建的队列的句柄
往队列发布消息
BaseType_t xQueueSend( QueueHandle_t xQueue,
const void * const pvItemToQueue,
TickType_t xTicksToWait)该函数用于往一个指定的队列中发送一个数据,数据将被放在队列的队尾,一共有三个参数:
xQueue:队列句柄
pvItemToQueue:需要发送的消息的指针(内存地址);
xTicksToWait:如果队列当前已满,最长等待多长时间,单位是tick
返回值为pdTRUE时,表示消息发布成功,返回值为pdFALSE时,表示消息发布失败。
从队列消费消息
BaseType_t xQueueReceive( QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait )该函数用于从队列中取出一个消息,默认是从队列头取出消息,一共有三个参数:
xQueue:需要取消息的队列的句柄
pvBuffer:取出的消息需要存放的指针(内存地址)
xTicksToWait:如果队列为空,最长等待时间,单位为tick
返回值为pdTRUE时,表示成功从队列取出消息,返回值为pdFALSE时,表示当前队列为空,暂时取不到消息。
获取队列当前可用空间
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue )该函数用于获取一个队列当前可用空间,只有一个参数:
xQueue:要获取的队列的句柄
返回值为当前队列可用空间
实战演练
实现如下需求:
模拟海底捞接客流程,一个客户去用餐,首先去门迎服务台签到,门迎服务台的服务员会给客户一个号牌,并要求客户在叫号区等待;当有用餐座位时,餐饮服务员会从叫号区按顺序叫客户去用餐,叫号区最大可容纳10人。
#include <Arduino.h>
struct Consumer
{
uint16_t number; // 客户的编号
char type[10]; // 坐席的类型
};
QueueHandle_t queue;
uint16_t number = 1;
void send(void *ptr)
{
while (1)
{
Consumer consumer;
consumer.number = number++;
strcpy(consumer.type, "大桌");
if (xQueueSend(queue, &consumer, 0) == pdTRUE)
{
Serial.printf("欢迎客户: %d(%s), 当前空余位置: %d\n", consumer.number, consumer.type, uxQueueSpacesAvailable(queue));
}
else
{
Serial.println("叫号区已经没有空位");
}
vTaskDelay(random(1000));
}
}
void receive(void *ptr)
{
while (1)
{
Consumer consumer;
if (xQueueReceive(queue, &consumer, 0) == pdTRUE)
{
Serial.printf("有请客户用餐: %d(%s)\n", consumer.number, consumer.type);
vTaskDelay(1000 + random(1000));
}
}
}
void setup()
{
Serial.begin(115200);
queue = xQueueCreate(10, sizeof(Consumer)); // 创建一个队列
xTaskCreate(send, "send", 2048, nullptr, 1, NULL); // 模拟门迎服务台服务员的任务
xTaskCreate(receive, "receive", 2048, nullptr, 1, NULL); // 模拟用餐服务员的任务
}
void loop()
{
}
运行结果如下:
