单片机开发进阶技巧:(三)生产者-消费者模型
上一篇下一篇如何判断一个浮点型运算结果是否为整数目 录生产者-消费者模型1定义2核心同步问题3实现方式3.1基于信号量的经典方案3.2在 RTOS 中如 FreeRTOS4实际工作中常见的应用场景5总结生产者-消费者模型1定义生产者-消费者模型 是并发编程中最经典的设计模式之一用于解决多线程/多进程之间数据传递与协作的问题。生产者Producer负责生成数据比如传感器采集、网络接收、文件读取。消费者Consumer负责处理数据比如计算、显示、存储、转发。共享缓冲区Buffer一个固定大小的队列或环形缓冲区生产者把数据放进去消费者从里面取出来。三者关系[生产者] —(写入)— [共享缓冲区] —(读取)— [消费者]。为什么需要这个模型好处说明解耦生产者和消费者互不依赖可以独立变化平衡速度差异生产者快、消费者慢时数据暂存缓冲区不会丢失或阻塞异步处理生产者在产生数据后不必等消费者处理完可以立刻去做下一件事支持并发多个生产者和多个消费者可以同时工作2核心同步问题共享缓冲区是临界资源需要解决两个问题问题描述典型解决方式互斥同一时刻只能有一个线程修改缓冲区互斥锁mutex同步缓冲区满时生产者必须等待缓冲区空时消费者必须等待信号量semaphore3实现方式3.1基于信号量的经典方案// 假设有一个长度为 N 的环形缓冲区sem_tempty_slots;// 初始值为 N空位数sem_tfilled_slots;// 初始值为 0已填数据数mutex_tbuffer_mutex;// 保护缓冲区操作voidproducer(){while(1){itemproduce_item();sem_wait(empty_slots);// 等有空位mutex_lock(buffer_mutex);insert_item(item);// 放入缓冲区mutex_unlock(buffer_mutex);sem_post(filled_slots);// 通知有新数据}}voidconsumer(){while(1){sem_wait(filled_slots);// 等有数据mutex_lock(buffer_mutex);itemremove_item();// 取出缓冲区mutex_unlock(buffer_mutex);sem_post(empty_slots);// 通知有空位consume_item(item);}}3.2在 RTOS 中如 FreeRTOS通常会用消息队列Queue直接实现队列本身已经内置了互斥和同步机制QueueHandle_t xQueuexQueueCreate(QUEUE_LENGTH,sizeof(Data_t));// 生产者voidvProducerTask(void*pvParameters){Data_t data;// ... 生成数据xQueueSend(xQueue,data,portMAX_DELAY);}// 消费者voidvConsumerTask(void*pvParameters){Data_t data;xQueueReceive(xQueue,data,portMAX_DELAY);// ... 处理数据}4实际工作中常见的应用场景场景生产者消费者缓冲区日志系统各个业务线程产生日志单独的日志线程写入文件无锁环形缓冲区串口通信串口接收中断ISR数据解析线程RingBuffer网络包处理网卡驱动接收数据协议栈线程LwIPpbuf 链表 / 队列传感器数据采集定时器中断采集 ADC数据处理/上传线程双缓冲或队列5总结生产者消费者模型就是通过一个共享的缓冲区把“生产数据”和“消费数据”两个动作分开让它们可以并发、异步地执行并利用互斥和同步机制保证数据安全与流程协调。