📘 简介

本模块实现了一个线程安全环形缓冲区(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++ 项目,提升嵌入式跨平台性。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐