线程之间的通信方式总结(Linux&&FreeRTOS)
Linux环境下线程之间通信方式
1. 共享内存(Shared Memory)
这是最直接的方式,多个线程访问同一块内存区域(如全局变量、类的成员变量等)。核心机制:配合锁机制(如互斥锁 Mutex、自旋锁 Spinlock、读写锁 RWLock)或原子操作(Atomic)来防止竞态条件。- 高性能计算:对性能要求极高,需要频繁读写数据且延迟敏感的场景(如高频交易、实时信号处理)。
- 状态共享:多个线程需要共同维护一个复杂的状态对象时。
- 注意:必须严格处理同步问题,否则容易导致数据错乱。
2. 消息队列(Message Queue)
线程不直接共享内存,而是通过一个中间缓冲区(队列)传递消息或任务对象。核心机制:生产者线程将数据放入队列,消费者线程从队列取出数据。通常配合互斥锁和条件变量实现。- 解耦:生产者和消费者逻辑独立,互不依赖,便于模块化开发。
- 流量削峰:当生产者速度远快于消费者时,队列可以作为缓冲,防止系统崩溃。
- 任务分发:典型的“生产者 - 消费者”模型,如线程池中的任务调度。
3. 管道(Pipe)与 消息通道(Channel)
管道:通常用于进程间通信,但在某些语言或框架中也可用于线程(如 Unix 域套接字或特定语言的线程管道)。消息通道(如 Go 的 Channel):这是“不要通过共享内存来通信,而是通过通信来共享内存”理念的体现。- 并发协程模型:在 Go 或 Rust 等语言中,Channel 是构建高并发无锁程序的首选,适合复杂的多阶段流水线处理。
4. 信号量(Semaphore)与 条件变量(Condition Variable)
这两者主要用于同步控制,而非直接传递大量数据,但它们是线程通信的基础设施。条件变量:线程等待某个条件成立(如“缓冲区不为空”)后再继续执行。- 等待特定事件:如主线程等待所有子线程完成计算后汇总结果(join 或 barrier 模式)。
- 生产者 - 消费者同步:配合共享内存或队列使用,解决“空队列等待”或“满队列等待”的问题。
5. 信号(Signal)与 中断机制
操作系统级别的信号机制,或编程语言提供的中断/取消机制(如 Thread.interrupt())。- 异步通知:当发生硬件中断或系统级事件时,通知线程进行紧急处理。
- 注意:这种方式通常比较底层,处理不当容易引发资源泄露,需谨慎使用。
6. 原子变量(Atomic Variables)
利用 CPU 指令集提供的原子操作(如 CAS - Compare And Swap)。- 优点:比互斥锁开销更小,性能更高,但仅适用于简单的读写操作,无法处理复杂逻辑。
FreeRTOS环境下任务之间的通信方式
1. 队列(Queues)
这是 FreeRTOS 中最核心、最通用的通信机制。队列不仅用于传递数据,还可以用于传递句柄(如互斥锁、信号量句柄)或任务句柄。机制特点:支持先进先出(FIFO),也支持后进先出(LIFO)。支持阻塞等待:如果队列空/满,任务可以进入阻塞状态,直到有数据/空间,这能极大降低 CPU 占用率。支持数据拷贝:发送时将数据拷贝到队列,接收时拷贝出来,保证数据安全性。支持队列集(Queue Sets):可以等待多个队列或信号量中的任意一个就绪。- 生产者 - 消费者模型:一个任务产生数据,另一个任务处理数据。
2. 二值信号量(Binary Semaphores)
- 任务同步:例如,一个任务等待某个事件发生(如传感器数据就绪),另一个任务在事件发生时“释放”信号量。
- 中断同步:在中断服务程序(ISR)中释放信号量,唤醒等待该事件的任务进行处理。
3. 计数信号量(Counting Semaphores)
维护一个计数值,每次 Take 减 1,Give 加 1。- 资源计数:管理有限数量的共享资源(如串口、ADC 通道、缓冲区槽位)。
- 事件统计:统计某个事件发生的次数,任务等到次数达到阈值后再执行。
4. 互斥量(Mutexes)
专门用于资源保护的信号量,具有优先级继承(Priority Inheritance)机制。优先级继承:当高优先级任务等待低优先级任务持有的互斥量时,低优先级任务会临时提升优先级,防止“优先级反转”问题。- 临界区保护:保护共享资源(如全局变量、硬件寄存器、共享数据结构)不被多个任务同时访问。
- 解决优先级反转:在复杂的优先级调度系统中保护关键资源。
5. 递归互斥量(Recursive Mutexes)
记录获取次数,每次 Take 增加计数,Give 减少计数,直到计数为 0 才真正释放。- 嵌套调用:当任务中的函数 A 调用了函数 B,而两者都需要访问同一个共享资源时,避免死锁。
6. 事件组(Event Groups)
一个事件组包含多个位(Bit,通常 8 位或 24 位),每一位代表一个独立的事件。任务可以等待任意一个或多个位被置位(ANY 或 ALL 模式)。- 多事件组合:任务需要等待多个条件同时满足(例如:既收到数据,又检测到错误标志)。
- 广播通知:一个任务触发事件,唤醒多个等待该事件的任务。
7. 流缓冲区(Stream Buffers)与 消息缓冲区(Message Buffers)
这是 FreeRTOS V9.0.0 引入的优化机制,专为字节流和消息设计。流缓冲区:专为字节流(如 UART 数据、网络数据包)设计,无消息边界,写一次读一次,性能极高,内存开销小。消息缓冲区:专为定长或变长消息设计,保留消息边界,支持多生产者/多消费者(需配合锁或逻辑控制)。两者都支持中断安全操作(ISR Safe),且比队列更高效(因为避免了额外的内存管理开销和拷贝)。- 消息缓冲区:任务间传递带有长度信息的消息(如传感器数据包),替代队列以获得更高吞吐量。
8. 任务通知(Task Notifications)
FreeRTOS V8.2 引入的轻量级机制,直接利用任务控制块(TCB)中的字段。零内存开销:不需要像队列或信号量那样动态分配内存。速度最快:比信号量更快,因为减少了系统调用和内存访问。每个任务最多有 5 个通知值(Value)和 1 个通知状态位(State)。- 简单的同步:替代二值信号量,用于 ISR 唤醒任务。
- 带数据的通知:替代二值信号量或小队列,直接传递一个简单的整数值。
- 资源极度受限:无法为队列或信号量分配堆内存的场景。