ESP32生产者消费者双队列实现报错及同步方案咨询
ESP32 FreeRTOS双队列生产者/消费者问题排查与修复
核心错误定位
assert failed: xQueueGenericSend queue.c:820 (pxQueue) 这个错误的直接原因是调用xQueueGenericSend时传入的队列句柄无效(大概率是NULL),先从队列初始化和句柄切换逻辑入手排查。
分步解决方案
1. 确保队列正确初始化
首先检查两个队列的创建逻辑,必须保证xQueueCreate返回有效句柄,避免后续使用空指针:
#define QUEUE_SIZE 10 typedef struct { // 你的数据结构定义 int sensor_data; } DataStruct; QueueHandle_t queue1, queue2; void queue_init() { queue1 = xQueueCreate(QUEUE_SIZE, sizeof(DataStruct)); queue2 = xQueueCreate(QUEUE_SIZE, sizeof(DataStruct)); if (queue1 == NULL || queue2 == NULL) { ESP_LOGE("QUEUE", "Failed to create queues, insufficient memory"); abort(); // 初始化失败直接终止,避免后续崩溃 } }
- 不要在任务内部重复创建队列,也不要在任何地方将队列句柄置空。
2. 修正双队列切换的非阻塞控制逻辑
你的核心需求是生产者不能阻塞,因此不能用普通的阻塞式信号量/互斥锁逻辑,需要用非阻塞锁配合队列状态判断来实现切换:
QueueHandle_t prod_queue; // 当前生产者使用的队列 QueueHandle_t cons_queue; // 当前消费者使用的队列 SemaphoreHandle_t queue_lock; // 保护队列切换的互斥锁 void app_main() { queue_init(); queue_lock = xSemaphoreCreateMutex(); prod_queue = queue1; cons_queue = queue2; xTaskCreate(producer_task, "producer", 4096, NULL, 5, NULL); xTaskCreate(consumer_task, "consumer", 4096, NULL, 4, NULL); } void producer_task(void *arg) { DataStruct data; while (1) { // 非阻塞获取锁,保证生产者不会被卡住 if (xSemaphoreTake(queue_lock, 0) == pdTRUE) { // 检查当前生产队列是否已满,满则切换到另一个队列 if (uxQueueMessagesWaiting(prod_queue) == QUEUE_SIZE) { // 交换生产/消费队列(前提是消费队列已被处理完毕,可根据需求调整) QueueHandle_t temp = prod_queue; prod_queue = cons_queue; cons_queue = temp; } // 非阻塞写入数据,失败则丢弃(符合生产者不阻塞的要求) data.sensor_data = read_i2c_sensor(); // 你的I2C读取函数 xQueueSend(prod_queue, &data, 0); xSemaphoreGive(queue_lock); } // 短延时避免空转占用过多CPU vTaskDelay(pdMS_TO_TICKS(1)); } } void consumer_task(void *arg) { DataStruct data; while (1) { // 消费者可以阻塞等待锁,因为本身处理速度慢 xSemaphoreTake(queue_lock, portMAX_DELAY); // 清空当前消费队列的所有数据 while (xQueueReceive(cons_queue, &data, 0) == pdTRUE) { // 处理数据(比如写入文件) save_data_to_file(&data); } xSemaphoreGive(queue_lock); // 根据文件操作耗时调整延时 vTaskDelay(pdMS_TO_TICKS(100)); } }
- 生产者全程使用非阻塞API(
xSemaphoreTake和xQueueSend的超时参数设为0),确保不会被阻塞。 - 互斥锁仅用于保护队列切换的逻辑,避免两个任务同时操作同一个队列。
3. 其他潜在问题排查
- 检查是否有代码调用
vQueueDelete删除队列,导致句柄失效。 - 确认队列元素大小与写入的数据类型大小完全匹配,内存不匹配可能导致句柄被破坏。
- 验证任务栈大小是否足够,栈溢出可能会篡改全局的队列句柄变量。
内容的提问来源于stack exchange,提问作者Pali775
相关产品推荐
相关产品推荐

