嵌入式常用数据结构——ringbuffer环形缓冲区(线程安全版本带锁)
📘 简介
本模块实现了一个线程安全的环形缓冲区(RingBuffer),适用于音频数据、串口通信、生产者-消费者等场景。完整的.c和.h文件已经提供,核心特点包括:
- 支持多线程并发读写(使用 pthread_mutex 加锁)
- 固定容量,FIFO 顺序读写
- 写满时不覆盖旧数据,自动丢弃新数据
- 高性能、轻量级、适用于嵌入式或语音处理应用

🚀完整代码
完整代码包括.c和.h
#ifndef RINGBUFFER_H
#define RINGBUFFER_H
#include <stddef.h>
#include <stdint.h>
#include <pthread.h> // 添加 pthread.h 头文件
typedef struct {
uint8_t *buffer;
size_t size;
size_t read_pos;
size_t write_pos;
size_t count;
pthread_mutex_t mutex; // 新增互斥锁
} RingBuffer;
RingBuffer *ringbuffer_init(size_t size);
void ringbuffer_free(RingBuffer *rb);
size_t ringbuffer_write(RingBuffer *rb, const uint8_t *data, size_t len);
size_t ringbuffer_read(RingBuffer *rb, uint8_t *data, size_t len);
size_t ringbuffer_available(RingBuffer *rb);
#endif // RINGBUFFER_H
/*
* @Author:
* @Date: 2025-06-24 10:59:23
* @LastEditors: Kunlun-Donkey
* @LastEditTime: 2025-06-24 16:10:35
* @FilePath: \vosk_wakeup_demo (1)\src\ringbuffer.c
* @Description: Implementation of a thread-safe ring buffer for audio data buffering.
*/
#include "ringbuffer.h"
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
// 初始化环形缓冲区,分配内存并初始化互斥锁
RingBuffer *ringbuffer_init(size_t size) {
RingBuffer *rb = malloc(sizeof(RingBuffer));
rb->buffer = malloc(size);
rb->size = size;
rb->read_pos = 0;
rb->write_pos = 0;
rb->count = 0;
pthread_mutex_init(&rb->mutex, NULL); // 初始化锁
return rb;
}
// 释放环形缓冲区资源,销毁互斥锁
void ringbuffer_free(RingBuffer *rb) {
pthread_mutex_destroy(&rb->mutex); // 销毁锁
free(rb->buffer);
free(rb);
}
// 写入数据到环形缓冲区,写入长度不超过剩余空间,线程安全
size_t ringbuffer_write(RingBuffer *rb, const uint8_t *data, size_t len) {
pthread_mutex_lock(&rb->mutex);
size_t written = 0;
while (written < len && rb->count < rb->size) {
rb->buffer[rb->write_pos++] = data[written++];
rb->write_pos %= rb->size;
rb->count++;
}
pthread_mutex_unlock(&rb->mutex);
return written;
}
// 从环形缓冲区读取数据,读取长度不超过已有数据量,线程安全
size_t ringbuffer_read(RingBuffer *rb, uint8_t *data, size_t len) {
pthread_mutex_lock(&rb->mutex);
size_t read = 0;
while (read < len && rb->count > 0) {
data[read++] = rb->buffer[rb->read_pos++];
rb->read_pos %= rb->size;
rb->count--;
}
pthread_mutex_unlock(&rb->mutex);
return read;
}
// 查询环形缓冲区当前可读数据量,线程安全
size_t ringbuffer_available(RingBuffer *rb) {
pthread_mutex_lock(&rb->mutex);
size_t count = rb->count;
pthread_mutex_unlock(&rb->mutex);
return count;
}
/*
注意:
如果 ringbuffer 写入速度大于读取速度,缓冲区会被写满。
此时 ringbuffer_write 只会写入剩余空间允许的数据,多余的数据会被丢弃(不会覆盖未读数据)。
即:新数据丢弃,旧数据保留,直到有空间被读取出来。
*/
// 以下为 ringbuffer 调用 demo,仅供内部测试写8个数据,读取6个,调用代码已屏蔽
#if 0
#include <stdio.h>
int main() {
RingBuffer *rb = ringbuffer_init(16);
uint8_t wdata[8] = {1,2,3,4,5,6,7,8};
uint8_t rdata[6] = {0};
ringbuffer_write(rb, wdata, 8);
printf("可读数据量: %zu\n", ringbuffer_available(rb));
ringbuffer_read(rb, rdata, 6);
printf("读取后可读数据量: %zu\n", ringbuffer_available(rb));
printf("读取到的数据: ");
for (int i = 0; i < 6; ++i) {
printf("%d ", rdata[i]);
}
printf("\n");
ringbuffer_free(rb);
return 0;
}
/*
预估输出:
可读数据量: 8
读取后可读数据量: 2
读取到的数据: 1 2 3 4 5 6
*/
#endif
📚 接口说明
RingBuffer *ringbuffer_init(size_t size);
创建并初始化一个指定容量的环形缓冲区。
- size: 缓冲区大小(字节)
- 返回:RingBuffer 指针
void ringbuffer_free(RingBuffer *rb);
销毁缓冲区,释放内存资源。
size_t ringbuffer_write(RingBuffer *rb, const uint8_t *data, size_t len);
写入数据到缓冲区,若剩余空间不足,则部分写入或丢弃新数据。
data: 写入数据指针
len: 要写入的字节数
返回:实际写入的字节数
size_t ringbuffer_read(RingBuffer *rb, uint8_t *data, size_t len);
从缓冲区读取数据,读取不会超过当前有效数据量。
data: 读取缓冲区指针
len: 最大读取长度
返回:实际读取的字节数
size_t ringbuffer_available(RingBuffer *rb);
查询当前可读的数据字节数。
🧠 工作原理
RingBuffer 结构内部采用:
固定大小的字节数组 buffer
读写指针 read_pos / write_pos
有效字节计数 count
互斥锁 pthread_mutex_t 实现线程安全
采用以下规则:
写入:只在 count < size 时写入,超出部分丢弃
读取:只在 count > 0 时读取,支持部分读取
🔒 线程安全说明
所有 read/write/available 操作均通过 pthread_mutex 加锁,支持典型的 双线程模型:
线程 A:采集数据并调用 ringbuffer_write
线程 B:读取数据并调用 ringbuffer_read
无需额外同步机制即可安全并发读写。
🧪 使用示例
#include "ringbuffer.h"
int main() {
RingBuffer *rb = ringbuffer_init(16);
uint8_t wdata[8] = {1,2,3,4,5,6,7,8};
uint8_t rdata[6] = {0};
ringbuffer_write(rb, wdata, 8);
printf("可读数据量: %zu\n", ringbuffer_available(rb));
ringbuffer_read(rb, rdata, 6);
printf("读取后可读数据量: %zu\n", ringbuffer_available(rb));
printf("读取到的数据: ");
for (int i = 0; i < 6; ++i) {
printf("%d ", rdata[i]);
}
printf("\n");
ringbuffer_free(rb);
return 0;
}
示例输出:
可读数据量: 8 读取后可读数据量: 2 读取到的数据: 1 2 3 4 5 6
⚠️ 注意事项
写入速度持续大于读取速度时,缓冲区可能满。此时超出部分数据会被丢弃(不覆盖旧数据)。
当前版本不支持阻塞式读写,如需支持阻塞可拓展为条件变量版。
✅ 后续可拓展点
1. 线程安全性能优化
当前每次读写都全程加锁,可以考虑细粒度锁或原子变量方案(比如基于 stdatomic.h 的无锁版本),提高高频写入时的性能。
2. 添加 ringbuffer_peek() 方法
支持“读取不移动读指针”的功能,常用于语音识别帧窗口滑动等场景。
size_t ringbuffer_peek(RingBuffer *rb, uint8_t *data, size_t len);
3. 添加 ringbuffer_clear() 方法,
清空 buffer 内容,常用于状态重置或超时恢复。
void ringbuffer_clear(RingBuffer *rb);
4. 支持读写统计信息
比如:总写入字节数、丢弃次数、溢出次数,可用于性能监控。
5. 内存分配失败检查
ringbuffer_init() 中未对 malloc() 的返回值做判空检查,建议加上:
if (!rb || !rb->buffer) {
free(rb);
return NULL;
}
6. 适配 C++
提供 extern “C” 以兼容 C++ 项目,提升嵌入式跨平台性。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)