1. 课程导入

在嵌入式开发中,我们经常面对这样的需求:一个 MCU 既要每 10ms 采集一次传感器数据,又要响应串口命令,还要驱动显示屏刷新,并在异常时立即切断电机。如果把这些工作全部塞进一个 while(1) 主循环,代码会迅速演变成难以维护的“时间片面条”。并发编程正是为了解决这类多任务共享处理器、共享外设、共享内存的问题而存在。

本课程的目标不是单纯讲解 C++ 语法,而是建立一套可在资源受限设备上落地的并发与任务架构能力:能识别并发需求,能选择合适模型,能写出可预测、可测试、可复盘的嵌入式并发代码。

一、核心概念:并发、并行与实时性

并发(Concurrency)指多个任务在逻辑上同时推进;并行(Parallelism)指多个任务在物理上同时执行。单核 MCU 上通常只有并发,没有并行,靠调度器快速切换制造“同时运行”的假象。理解这一点,才能明白为什么共享资源的保护如此重要。

嵌入式并发还多了一个硬约束:实时性。任务不仅要“做完”,还要“按时做完”。因此我们需要关注优先级、阻塞时间、抖动和截止期,而不仅仅是平均吞吐量。

并发三要素:任务划分、任务间通信、共享资源保护。任何嵌入式并发架构,本质上都是对这三件事的组织方式。

二、整体知识框架

本课程的知识框架可以分成四层:语言层、RTOS 层、架构层和验证层。语言层提供 std::thread、std::mutex、std::atomic、std::condition_variable 等工具;RTOS 层提供任务、队列、信号量、事件组;架构层决定任务如何划分、如何通信、如何降级;验证层则通过日志、断言、静态分析和压力测试保证行为可预测。

嵌入式 C++ 并发与任务架构 · 知识框架 语言层 std::thread · std::mutex · std::atomic · condition_variable · future/promise 提供并发原语,但需注意嵌入式工具链支持程度与运行时开销 RTOS 层 任务/线程 · 优先级 · 队列 · 信号量 · 互斥量 · 事件组 · 软件定时器 FreeRTOS / Zephyr / RT-Thread 等提供调度与同步机制 架构层 任务划分 · 生产者-消费者 · 发布-订阅 · 状态机 · 优先级反转规避 · 降级策略 决定系统是否可维护、可扩展、可预测 验证层 日志追踪 · 断言 · 静态分析 · 竞态检测 · 压力测试 · 时序测量 让并发缺陷从“偶发”变成“可复现、可定位”

三、典型应用场景

嵌入式并发并非抽象概念,它出现在大量真实产品中。下表列出常见场景与对应的并发关注点:

应用场景 并发任务 关键问题
电机控制 PWM 更新、电流采样、故障保护 高优先级保护必须抢占,共享 ADC 数据需原子访问
工业网关 Modbus 采集、MQTT 上报、本地缓存 队列背压、网络阻塞不能拖垮采集任务
智能穿戴 传感器融合、BLE 通信、屏幕刷新 低功耗与实时响应之间的平衡
车载控制器 CAN 收发、诊断、执行器控制 优先级反转、死锁、时序可预测性

四、一个最小并发案例

下面是一个典型的“采集-处理”双任务结构。采集任务以固定周期读取传感器,处理任务从队列取数据。注意:队列是任务间通信的边界,也是共享资源保护的第一道防线。

// 伪代码:FreeRTOS 风格的双任务结构
QueueHandle_t g_sensorQueue;

void TaskAcquire(void *pv) {
    SensorData d;
    for (;;) {
        d = read_sensor();              // 周期采集
        xQueueSend(g_sensorQueue, &d, 0); // 非阻塞入队
        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

void TaskProcess(void *pv) {
    SensorData d;
    for (;;) {
        if (xQueueReceive(g_sensorQueue, &d, portMAX_DELAY)) {
            process(d);                 // 消费数据
        }
    }
}

这段代码看似简单,却已经包含了并发编程的核心问题:任务优先级如何设置?队列满了怎么办?process() 执行时间过长会不会导致采集丢数据?这些问题将在后续章节逐一展开。

学习建议:每学一个并发原语,都问自己三个问题——它解决什么问题?它的开销是多少?它在最坏情况下会阻塞多久?

五、常见误区与复盘

  • 误区一:任务越多越好。任务数量增加会带来栈内存开销和调度抖动,嵌入式系统应尽量合并职责相近的任务。
  • 误区二:用全局变量传数据。不加保护的全局变量是竞态条件的温床,应优先使用队列、邮箱或原子类型。
  • 误区三:忽视优先级反转。低优先级任务持有互斥量时,可能阻塞高优先级任务,需使用优先级继承或设计上避免长临界区。
  • 误区四:只在实验室测试。并发缺陷往往在高负载、长时间运行后出现,必须做压力测试和时序测量。
并发编程的第一原则:先保证正确性,再优化性能。一个“跑得快但偶尔死机”的系统,在嵌入式领域是不可接受的。