You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 19:22:58