设计一个状态机

本人也工作三个月了,感觉到了状态机的重要,想要学习状态机的设计,目前的想法如下:如果大家有好思路,或者好的教程,可以推荐给我。以下作为分析,有问题大家提出了:

第一版本

举例子, 整体的框架为:
TempTask 协调器 NetTask BatteryTask SafetyTask Worker

1.模块状态定义

每个模块有自己的枚举状态:

c 复制代码
typedef enum {
    TEMP_NORMAL,
    TEMP_HIGH
} TempState;

typedef enum {
    NET_CONNECTED,
    NET_DISCONNECTED
} NetState;

typedef enum {
    BATTERY_NORMAL,
    BATTERY_LOW,
    BATTERY_CHARGING
} BatteryState;

typedef enum {
    SAFETY_OK,
    SAFETY_ALERT
} SafetyState;

typedef enum {
    GLOBAL_RUN,
    GLOBAL_COOLING,
    GLOBAL_WAIT_NETWORK,
    GLOBAL_LOW_POWER,
    GLOBAL_SAFETY_ALERT
} GlobalState;

2.每个模块的任务和状态机

温度任务 TempTask

c 复制代码
void TempTask(void *pvParameters) {
    TempState tempState = TEMP_NORMAL;

    for (;;) {
        int temp = getTemperatureSensor();
        if (temp > 80) tempState = TEMP_HIGH;
        else tempState = TEMP_NORMAL;

        xQueueOverwrite(tempQueue, &tempState);
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

网络任务 NetTask

c 复制代码
void NetTask(void *pvParameters) {
    NetState netState = NET_CONNECTED;

    for (;;) {
        if (!isNetworkConnected()) netState = NET_DISCONNECTED;
        else netState = NET_CONNECTED;

        xQueueOverwrite(netQueue, &netState);
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

电池任务 BatteryTask

c 复制代码
void BatteryTask(void *pvParameters) {
    BatteryState batteryState = BATTERY_NORMAL;

    for (;;) {
        int level = getBatteryLevel();
        if (level < 20) batteryState = BATTERY_LOW;
        else if (isCharging()) batteryState = BATTERY_CHARGING;
        else batteryState = BATTERY_NORMAL;

        xQueueOverwrite(batteryQueue, &batteryState);
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

安全任务 SafetyTask

c 复制代码
void SafetyTask(void *pvParameters) {
    SafetyState safetyState = SAFETY_OK;

    for (;;) {
        if (!isSafetyCoverClosed()) safetyState = SAFETY_ALERT;
        else safetyState = SAFETY_OK;

        xQueueOverwrite(safetyQueue, &safetyState);
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

3.中央协调器任务 CoordinatorTask

逻辑:

  • 收集所有模块状态
  • 基于优先级做全局状态判断(先安全 → 再电量 → 再温度 → 再网络)
  • 将全局状态发送给 WorkerTask
c 复制代码
void CoordinatorTask(void *pvParameters) {
    GlobalState globalState = GLOBAL_RUN;

    TempState tempState;
    NetState netState;
    BatteryState batteryState;
    SafetyState safetyState;

    for (;;) {
        xQueuePeek(tempQueue, &tempState, 0);
        xQueuePeek(netQueue, &netState, 0);
        xQueuePeek(batteryQueue, &batteryState, 0);
        xQueuePeek(safetyQueue, &safetyState, 0);

        if (safetyState == SAFETY_ALERT) {
            globalState = GLOBAL_SAFETY_ALERT;
        }
        else if (batteryState == BATTERY_LOW) {
            globalState = GLOBAL_LOW_POWER;
        }
        else if (tempState == TEMP_HIGH) {
            globalState = GLOBAL_COOLING;
        }
        else if (netState == NET_DISCONNECTED) {
            globalState = GLOBAL_WAIT_NETWORK;
        }
        else {
            globalState = GLOBAL_RUN;
        }

        xQueueOverwrite(globalQueue, &globalState);
        vTaskDelay(pdMS_TO_TICKS(200));
    }
}

4.主业务任务 WorkerTask

c 复制代码
void WorkerTask(void *pvParameters) {
    GlobalState globalState;
    for (;;) {
        xQueueReceive(globalQueue, &globalState, portMAX_DELAY);

        switch (globalState) {
            case GLOBAL_RUN:
                printf("[Worker] 系统运行正常\n");
                break;
            case GLOBAL_COOLING:
                printf("[Worker] 冷却模式开启\n");
                break;
            case GLOBAL_WAIT_NETWORK:
                printf("[Worker] 等待网络连接...\n");
                break;
            case GLOBAL_LOW_POWER:
                printf("[Worker] 电量低,进入节能模式\n");
                break;
            case GLOBAL_SAFETY_ALERT:
                printf("[Worker] 安全警告!停止所有运行\n");
                break;
        }
    }
}

5.系统初始化 main()

c 复制代码
int main(void) {
    tempQueue    = xQueueCreate(1, sizeof(TempState));
    netQueue     = xQueueCreate(1, sizeof(NetState));
    batteryQueue = xQueueCreate(1, sizeof(BatteryState));
    safetyQueue  = xQueueCreate(1, sizeof(SafetyState));
    globalQueue  = xQueueCreate(1, sizeof(GlobalState));

    xTaskCreate(TempTask, "Temp", 512, NULL, 2, NULL);
    xTaskCreate(NetTask, "Net", 512, NULL, 2, NULL);
    xTaskCreate(BatteryTask, "Battery", 512, NULL, 2, NULL);
    xTaskCreate(SafetyTask, "Safety", 512, NULL, 2, NULL);
    xTaskCreate(CoordinatorTask, "Coordinator", 512, NULL, 2, NULL);
    xTaskCreate(WorkerTask, "Worker", 512, NULL, 2, NULL);

    vTaskStartScheduler();
    for (;;);
}

发现的缺点:

  • 在中央协调器任务中,globalState只能响应一次,且会覆盖上一个的值。如果多个任务都要切换,那么该怎么办呢?
  • 新增模块需要改中央协调器代码,不够解耦,可以用事件发布订阅(Publish/Subscribe),中央协调器不用硬改

第二版本

1.Pub/Sub设计思路

原理:

  • 发布者(Publisher):某模块检测到状态变化后,发布一个事件到公共事件队列(EventQueue)
  • 订阅者(Subscriber):任何任务都可以从公共事件队列"订阅"事件并处理

好处:

  • 减少队列数量:所有事件走一个总队列
  • 模块解耦:发布者不关心谁会处理事件
  • 易扩展:新增模块只要发布事件;新增订阅者只要监听公共队列
  • 类似RTOS Event Bus:在大型系统里是常见模式

2.事件结构定义

我们先定义一个结构体来表示事件:

c 复制代码
typedef enum {
    MODULE_TEMP,
    MODULE_NET,
    MODULE_BATTERY,
    MODULE_SAFETY
} ModuleID;

typedef enum {
    EVENT_TEMP_NORMAL,
    EVENT_TEMP_HIGH,
    EVENT_NET_CONNECTED,
    EVENT_NET_DISCONNECTED,
    EVENT_BATTERY_NORMAL,
    EVENT_BATTERY_LOW,
    EVENT_BATTERY_CHARGING,
    EVENT_SAFETY_OK,
    EVENT_SAFETY_ALERT
} EventType;

typedef struct {
    ModuleID module;  // 事件来自哪个模块
    EventType event;  // 事件类型
    uint32_t timestamp; // 时间戳(可选)
    int value;        // 额外数据(例如温度值、电量等)
} EventMessage;

3.公共事件总线(队列)

我们用一个全局队列 eventQueue:

c 复制代码
QueueHandle_t eventQueue;

因为是公共队列,所有模块都把事件扔进同一个地方。

4.1 发布者示例(温度任务)

c 复制代码
void TempTask(void *pvParameters) {
    for (;;) {
        EventMessage msg;
        msg.module = MODULE_TEMP;
        msg.value = getTemperatureSensor();
        msg.timestamp = xTaskGetTickCount();

        if (msg.value > 80) 
            msg.event = EVENT_TEMP_HIGH;
        else 
            msg.event = EVENT_TEMP_NORMAL;
        xQueueSend(eventQueue, &msg, portMAX_DELAY);
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}
  • 读取传感器数据
  • 判断事件类型(高温或正常)
  • 发布事件到总队列

4.2 发布者示例(网络任务)

c 复制代码
void NetTask(void *pvParameters) {
    for (;;) {
        EventMessage msg;
        msg.module = MODULE_NET;
        msg.timestamp = xTaskGetTickCount();

        if (isNetworkConnected()) 
            msg.event = EVENT_NET_CONNECTED;
        else 
            msg.event = EVENT_NET_DISCONNECTED;

        msg.value = 0; // 网络状态用不到value
        xQueueSend(eventQueue, &msg, portMAX_DELAY);

        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

5. 订阅者(中央协调器)

c 复制代码
void CoordinatorTask(void *pvParameters) {
    GlobalState globalState = GLOBAL_RUN;

    for (;;) {
        EventMessage msg;
        if (xQueueReceive(eventQueue, &msg, portMAX_DELAY)) {
            // 根据事件决定全局状态
            switch (msg.module) {
                case MODULE_TEMP:
                    if (msg.event == EVENT_TEMP_HIGH)
                        globalState = GLOBAL_COOLING;
                    else
                        globalState = GLOBAL_RUN;
                    break;

                case MODULE_NET:
                    if (msg.event == EVENT_NET_DISCONNECTED)
                        globalState = GLOBAL_WAIT_NETWORK;
                    break;

                case MODULE_BATTERY:
                    if (msg.event == EVENT_BATTERY_LOW)
                        globalState = GLOBAL_LOW_POWER;
                    break;

                case MODULE_SAFETY:
                    if (msg.event == EVENT_SAFETY_ALERT)
                        globalState = GLOBAL_SAFETY_ALERT;
                    break;
            }

            // 发布全局状态到另一个管理队列(或直接调用 Worker)
            xQueueOverwrite(globalQueue, &globalState);
        }
    }
}

6.Worker任务(业务执行)

c 复制代码
void WorkerTask(void *pvParameters) {
    GlobalState globalState;
    for (;;) {
        xQueueReceive(globalQueue, &globalState, portMAX_DELAY);

        switch (globalState) {
            case GLOBAL_RUN:
                printf("[Worker] 正常运行\n");
                break;
            case GLOBAL_COOLING:
                printf("[Worker] 冷却模式开启\n");
                break;
            case GLOBAL_WAIT_NETWORK:
                printf("[Worker] 等待网络连接...\n");
                break;
            case GLOBAL_LOW_POWER:
                printf("[Worker] 电量低,进入节能模式\n");
                break;
            case GLOBAL_SAFETY_ALERT:
                printf("[Worker] 安全警告!停止运行\n");
                break;
        }
    }
}

7. 系统初始化 main()

c 复制代码
int main(void) {
    eventQueue  = xQueueCreate(16, sizeof(EventMessage));
    globalQueue = xQueueCreate(1,  sizeof(GlobalState));

    xTaskCreate(TempTask, "Temp", 512, NULL, 2, NULL);
    xTaskCreate(NetTask, "Net", 512, NULL, 2, NULL);
    xTaskCreate(BatteryTask, "Battery", 512, NULL, 2, NULL);
    xTaskCreate(SafetyTask, "Safety", 512, NULL, 2, NULL);
    xTaskCreate(CoordinatorTask, "Coordinator", 512, NULL, 2, NULL);
    xTaskCreate(WorkerTask, "Worker", 512, NULL, 2, NULL);

    vTaskStartScheduler();
    for (;;);
}

缺点

  • 单个状态机是没有什么问题,但是如果多个任务内部也有相应的状态机呢?

第二版plus

这里主要是增加一个优先级的情况。主要是为了根据不同的优先级,允许事件插队。整体的还是和上版一样的,主要是

1. 数据结构

EventMessage 结构中加上优先级字段:

c 复制代码
typedef enum {
    EVENT_PRI_LOW = 0,      //这里
    EVENT_PRI_NORMAL,
    EVENT_PRI_HIGH
} EventPriority;

typedef enum {
    MODULE_TEMP,
    MODULE_NET,
    MODULE_BATTERY,
    MODULE_SAFETY
} ModuleID;

typedef enum {
    EVENT_TEMP_NORMAL,
    EVENT_TEMP_HIGH,
    EVENT_NET_CONNECTED,
    EVENT_NET_DISCONNECTED,
    EVENT_BATTERY_NORMAL,
    EVENT_BATTERY_LOW,
    EVENT_BATTERY_CHARGING,
    EVENT_SAFETY_OK,
    EVENT_SAFETY_ALERT
} EventType;

typedef struct {
    ModuleID module;         // 来源模块
    EventType event;         // 事件类型
    EventPriority priority;  // 事件优先级  这里
    uint32_t timestamp;      // 时间戳
    int value;               // 额外数据
} EventMessage;

2. 全局队列定义

c 复制代码
QueueHandle_t eventQueueHigh;
QueueHandle_t eventQueueNormal;
QueueHandle_t eventQueueLow;
QueueHandle_t globalQueue; // 协调器->Worker的状态队列

3. 发布者统一接口

任何模块在发布事件时只需调用这个函数,选择优先级即可:

c 复制代码
void EventBus_Publish(EventMessage *msg) {
    switch (msg->priority) {
        case EVENT_PRI_HIGH:
            xQueueSend(eventQueueHigh, msg, portMAX_DELAY);
            break;
        case EVENT_PRI_NORMAL:
            xQueueSend(eventQueueNormal, msg, portMAX_DELAY);
            break;
        case EVENT_PRI_LOW:
        default:
            xQueueSend(eventQueueLow, msg, portMAX_DELAY);
            break;
    }
}

4. 发布者任务示例

基于3来使用,给出示例:

温度任务

高温事件 → 高优,高温正常 → 低优

c 复制代码
void TempTask(void *pvParameters) {
    for (;;) {
        EventMessage msg;
        msg.module = MODULE_TEMP;
        msg.value = getTemperatureSensor();
        msg.timestamp = xTaskGetTickCount();

        if (msg.value > 80) {
            msg.event = EVENT_TEMP_HIGH;
            msg.priority = EVENT_PRI_HIGH;
        } else {
            msg.event = EVENT_TEMP_NORMAL;
            msg.priority = EVENT_PRI_LOW;
        }

        EventBus_Publish(&msg);
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

网络任务

断网 → 中优,正常连接 → 低优

c 复制代码
void NetTask(void *pvParameters) {
    for (;;) {
        EventMessage msg;
        msg.module = MODULE_NET;
        msg.timestamp = xTaskGetTickCount();

        if (isNetworkConnected()) {
            msg.event = EVENT_NET_CONNECTED;
            msg.priority = EVENT_PRI_LOW;
        } else {
            msg.event = EVENT_NET_DISCONNECTED;
            msg.priority = EVENT_PRI_NORMAL;
        }

        EventBus_Publish(&msg);
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

5. 协调器订阅逻辑(优先取高优队列)

c 复制代码
void CoordinatorTask(void *pvParameters) {
    GlobalState globalState = GLOBAL_RUN;
    EventMessage msg;

    for (;;) {
        // 按优先级顺序取事件
        if (xQueueReceive(eventQueueHigh, &msg, 0) == pdPASS ||
            xQueueReceive(eventQueueNormal, &msg, 0) == pdPASS ||
            xQueueReceive(eventQueueLow, &msg, 0) == pdPASS) {

            switch (msg.module) {
                case MODULE_TEMP:
                    if (msg.event == EVENT_TEMP_HIGH)
                        globalState = GLOBAL_COOLING;
                    else if (globalState == GLOBAL_COOLING)
                        globalState = GLOBAL_RUN;
                    break;

                case MODULE_NET:
                    if (msg.event == EVENT_NET_DISCONNECTED)
                        globalState = GLOBAL_WAIT_NETWORK;
                    else if (globalState == GLOBAL_WAIT_NETWORK)
                        globalState = GLOBAL_RUN;
                    break;

                case MODULE_BATTERY:
                    if (msg.event == EVENT_BATTERY_LOW)
                        globalState = GLOBAL_LOW_POWER;
                    else if (globalState == GLOBAL_LOW_POWER)
                        globalState = GLOBAL_RUN;
                    break;

                case MODULE_SAFETY:
                    if (msg.event == EVENT_SAFETY_ALERT)
                        globalState = GLOBAL_SAFETY_ALERT;
                    break;
            }

            // 发布状态给 Worker
            xQueueOverwrite(globalQueue, &globalState);
        }

        vTaskDelay(pdMS_TO_TICKS(50)); // 加快响应速度
    }
}

6. Worker任务保持原有逻辑

c 复制代码
void WorkerTask(void *pvParameters) {
    GlobalState globalState;
    for (;;) {
        xQueueReceive(globalQueue, &globalState, portMAX_DELAY);
        switch (globalState) {
            case GLOBAL_RUN:
                printf("[Worker] 正常运行");
                break;
            case GLOBAL_COOLING:
                printf("[Worker] 冷却模式开启");
                break;
            case GLOBAL_WAIT_NETWORK:
                printf("[Worker] 等待网络连接...\n");
                break;
            case GLOBAL_LOW_POWER:
                printf("[Worker] 电量低,进入节能模式");
                break;
            case GLOBAL_SAFETY_ALERT:
                printf("[Worker] 安全警告!停止运行");
                break;
        }
    }
}

7.系统初始化

c 复制代码
int main(void) {
    eventQueueHigh   = xQueueCreate(10, sizeof(EventMessage));
    eventQueueNormal = xQueueCreate(10, sizeof(EventMessage));
    eventQueueLow    = xQueueCreate(10, sizeof(EventMessage));
    globalQueue      = xQueueCreate(1, sizeof(GlobalState));

    xTaskCreate(TempTask, "Temp", 512, NULL, 2, NULL);
    xTaskCreate(NetTask, "Net", 512, NULL, 2, NULL);
    xTaskCreate(BatteryTask, "Battery", 512, NULL, 2, NULL);
    xTaskCreate(SafetyTask, "Safety", 512, NULL, 2, NULL);
    xTaskCreate(CoordinatorTask, "Coordinator", 512, NULL, 3, NULL);
    xTaskCreate(WorkerTask, "Worker", 512, NULL, 2, NULL);

    vTaskStartScheduler();
    for (;;);
}

改造结果

  • 三个队列:eventQueueHigh、eventQueueNormal、eventQueueLow,按优先级明确分流
  • 发布接口简单:只需决定 priority 即会丢到对应队列
  • 消费顺序明确:Coordinator 始终先取高优队列 → 中优 → 低优
  • 性能可控:不需排序,也不会阻塞高优事件

    到这里了,以后再做想法优化,同时如果大家有好的思路可以分享
相关推荐
Voyager_45 小时前
算法学习记录03——二叉树学习笔记:从两道题看透后序位置的关键作用
笔记·学习·算法
我先去打把游戏先7 小时前
ESP32学习笔记(基于IDF):ESP32连接MQTT服务器
服务器·笔记·单片机·嵌入式硬件·学习·esp32
deng-c-f10 小时前
Linux C/C++ 学习日记(29):IO密集型与CPU密集型、CPU的调度与线程切换
linux·学习·线程·cpu·io密集·cpu密集
四谎真好看11 小时前
Java 黑马程序员学习笔记(进阶篇18)
java·笔记·学习·学习笔记
洋洋的笔记11 小时前
银行测试学习计划
学习
Allan_202512 小时前
数据库学习
数据库·学习
报错小能手13 小时前
linux学习笔记(43)网络编程——HTTPS (补充)
linux·网络·学习
报错小能手13 小时前
linux学习笔记(45)git详解
linux·笔记·学习
百锦再13 小时前
Vue Scoped样式混淆问题详解与解决方案
java·前端·javascript·数据库·vue.js·学习·.net