环形缓冲区设计

在嵌入式开发中经常会涉及资源抢占的问题,一般会有这些情景:

  • 1、输入和输出不一致
  • 2、一写多读

缓冲区还多用在网络收发中,经常会因为网络抖动,为了保证读取的稳定,收到的数据会先写入缓冲区,之后在缓冲区中读取数据,这样可以一定程度的抵抗网络抖动。当然,缓冲区的大小也是有限的,当缓冲区没有数据的时候,就没什么作用了。

下面是我用过的几种缓冲区设计:

1、针对固定块大小,使用二维数组来实现

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include "stdint.h"
#include "pthread.h"
#include "string.h"
#include "stdbool.h"
// #include "tphub_ringbuf.h"

#define DATA_SIZE 960
#define READ_SIZE 480

typedef struct {
    signed char **buffer;  // 二维数组
    size_t bufferSize;  // 缓冲区大小
    size_t writePos;  // 写入位置
    size_t readPos;   // 读取位置
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    bool dataAvailable;  // 表示缓冲区中是否有可读数据
    size_t dataSize;  // 每次拷贝的大小
    int firstRead;
} RingBuffer;

#define blockSize 2
bool stopReading = false;

/*
bufferSize :是块的数量
dataSize:每个块的大小
*/
int RingBuffer_Init(RingBuffer *ringBuffer, size_t bufferSize, size_t dataSize)
{
    if(ringBuffer == NULL)
    {
        return -1;
    }
    ringBuffer->buffer = (signed char**)malloc(bufferSize * sizeof(signed char*));
    if(ringBuffer->buffer == NULL)
    {
        return -1;
    }
    for (size_t i = 0; i < bufferSize; ++i) {
        ringBuffer->buffer[i] = (signed char*)malloc(dataSize);
        if (ringBuffer->buffer[i] == NULL)
        {
            for(size_t j = 0; j<i; j++)
            {
                free(ringBuffer->buffer[j]);
            }
            free(ringBuffer->buffer);
            return -1;
        }
    }
    ringBuffer->bufferSize = bufferSize;
    ringBuffer->dataSize = dataSize;
    ringBuffer->writePos = 0;
    ringBuffer->readPos = 0;
    ringBuffer->dataAvailable = false;
    ringBuffer->firstRead = 0;
    pthread_mutex_init(&ringBuffer->mutex, NULL);
    pthread_cond_init(&ringBuffer->cond, NULL);
    return 0;
}

bool RingBuffer_Write(RingBuffer *ringBuffer, const void *data, size_t dataSize)
{
    if(ringBuffer == NULL)
    {
        return -1;
    }
    pthread_mutex_lock(&ringBuffer->mutex);

    if ((ringBuffer->writePos + dataSize) % ringBuffer->bufferSize == ringBuffer->readPos)
    {
        pthread_mutex_unlock(&ringBuffer->mutex);
        return false; // 缓冲区已满,写入失败
    }

    for (size_t i = 0; i < dataSize; ++i)
    {
        memcpy(ringBuffer->buffer[ringBuffer->writePos], (char *)data + i * ringBuffer->dataSize, ringBuffer->dataSize);
        ringBuffer->writePos = (ringBuffer->writePos + 1) % ringBuffer->bufferSize;
    }

    ringBuffer->dataAvailable = true;
    pthread_cond_signal(&ringBuffer->cond);
    pthread_mutex_unlock(&ringBuffer->mutex);

    return true;
}

/*修改了读的代码*/
size_t RingBuffer_Read(RingBuffer *ringBuffer, void *outData, size_t maxSize)
{
    if(ringBuffer == NULL)
    {
        return -1;
    }
    pthread_mutex_lock(&ringBuffer->mutex);
    if (!ringBuffer->firstRead) {
        while (!ringBuffer->dataAvailable || (ringBuffer->writePos - ringBuffer->readPos) < blockSize)
        {
            pthread_cond_wait(&ringBuffer->cond, &ringBuffer->mutex);
        }
        ringBuffer->firstRead = true;
    }
    else
    {
        while (!ringBuffer->dataAvailable)
        {
            pthread_cond_wait(&ringBuffer->cond, &ringBuffer->mutex);
        }
    }
    size_t i;
    for (i = 0; i < maxSize && ringBuffer->readPos != ringBuffer->writePos; ++i)
    {
        memcpy((char *)outData + i * ringBuffer->dataSize, ringBuffer->buffer[ringBuffer->readPos], ringBuffer->dataSize);
        ringBuffer->readPos = (ringBuffer->readPos + 1) % ringBuffer->bufferSize;
    }

    if (ringBuffer->readPos == ringBuffer->writePos)
    {
        ringBuffer->dataAvailable = false;
    }
    pthread_mutex_unlock(&ringBuffer->mutex);

    return i; // 返回实际读取的数据大小
}

int RingBuffer_Destroy(RingBuffer *ringBuffer)
{
    if(ringBuffer == NULL)
    {
        return -1;
    }
    for (size_t i = 0; i < ringBuffer->bufferSize; ++i) 
    {
        if(ringBuffer->buffer[i] != NULL)
        {
            free(ringBuffer->buffer[i]);
        }
        else
        {
            return -1;
        }
    }
    free(ringBuffer->buffer);
    pthread_mutex_destroy(&ringBuffer->mutex);
    pthread_cond_destroy(&ringBuffer->cond);
    return 0;
}

RingBuffer ringBuffer;

void *writeThread(void *arg) {
    FILE *pcmFile = fopen("audio.pcm", "rb");
    if (pcmFile == NULL) {
        perror("Error opening input.pcm file");
        return NULL;
    }

    char data[DATA_SIZE];
    while (fread(data, sizeof(char), DATA_SIZE, pcmFile) == DATA_SIZE) {
        RingBuffer_Write(&ringBuffer, data, 2);
        usleep(128000); // 128ms
    }
    stopReading = true;

    fclose(pcmFile);
    return NULL;
}

void *readThread(void *arg) {
    FILE *pcmFile = fopen("output.pcm", "wb");
    if (pcmFile == NULL) {
        perror("Error opening output.pcm file");
        return NULL;
    }

    char outData[READ_SIZE];
    while (!stopReading) {
        RingBuffer_Read(&ringBuffer, outData, 1);
        fwrite(outData, sizeof(char), READ_SIZE, pcmFile);
        usleep(64000); // 64ms
    }

    fclose(pcmFile);
    return NULL;
}

int main() {
    RingBuffer_Init(&ringBuffer, 10, READ_SIZE);
    
    pthread_t writeTid, readTid;
    pthread_create(&writeTid, NULL, writeThread, NULL);
    pthread_create(&readTid, NULL, readThread, NULL);

    pthread_join(writeTid, NULL);
    pthread_join(readTid, NULL);

    RingBuffer_Destroy(&ringBuffer);

    return 0;
}

运行后结果如下:
在这里插入图片描述

2、使用共享内存实现缓冲区

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <pthread.h>

#define SHM_NAME "my_shared_memory"
#define SHM_SIZE 5000 // 假设足以存储10个spk帧
#define SPK_FRAME_SIZE 500
#define MIC_FRAME_SIZE 256

// 环形缓冲区结构
typedef struct
{
    char buffer[SHM_SIZE];
    int write_pos;
    int read_pos;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
} ring_buffer_t;

// 初始化环形缓冲区
void init_buffer(ring_buffer_t *rb)
{
    pthread_mutex_init(&rb->mutex, NULL);
    pthread_cond_init(&rb->cond, NULL);
    rb->write_pos = 0;
    rb->read_pos = 0;
}

// 写入缓冲区的函数
void write_to_buffer(ring_buffer_t *rb, const char *data, int size)
{
    pthread_mutex_lock(&rb->mutex);
    while ((rb->write_pos - rb->read_pos + SHM_SIZE) % SHM_SIZE >= SHM_SIZE - size)
    {
        // 缓冲区已满,覆盖旧数据
        rb->read_pos = (rb->read_pos + size) % SHM_SIZE;
        printf("Buffer is full, overwriting old data\n");
    }
    memcpy(rb->buffer + rb->write_pos, data, size);
    rb->write_pos = (rb->write_pos + size) % SHM_SIZE;
    pthread_cond_signal(&rb->cond);
    pthread_mutex_unlock(&rb->mutex);
}

// 从缓冲区读取数据的函数
void read_from_buffer(ring_buffer_t *rb, char *data, int size)
{
    pthread_mutex_lock(&rb->mutex);
    while ((rb->write_pos - rb->read_pos + SHM_SIZE) % SHM_SIZE < size)
    {
        printf("Buffer is empty, waiting for data\n");
        pthread_cond_wait(&rb->cond, &rb->mutex);
    }
    memcpy(data, rb->buffer + rb->read_pos, size);
    rb->read_pos = (rb->read_pos + size) % SHM_SIZE;
    pthread_mutex_unlock(&rb->mutex);
}

void *spk_thread(void *arg)
{
    usleep(1000000);
    ring_buffer_t *rb = (ring_buffer_t *)arg;
    char spk_data[SPK_FRAME_SIZE];

    while (1)
    {
        // 生成模拟的spk数据
        for (int i = 0; i < SPK_FRAME_SIZE; ++i)
        {
            spk_data[i] = rand() % 256;
        }

        write_to_buffer(rb, spk_data, SPK_FRAME_SIZE);

        printf("SPK: Wrote data to buffer\n");

        usleep(1000000); // 10ms
    }
    return NULL;
}

void *mic_thread(void *arg)
{
    ring_buffer_t *rb = (ring_buffer_t *)arg;
    char mic_data[MIC_FRAME_SIZE];

    while (1)
    {
        read_from_buffer(rb, mic_data, MIC_FRAME_SIZE);

        printf("MIC: Read data from buffer\n");

        usleep(500000); // 5ms
    }
    return NULL;
}

int main()
{
    int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    ftruncate(shm_fd, sizeof(ring_buffer_t));
    ring_buffer_t *rb = (ring_buffer_t *)mmap(0, sizeof(ring_buffer_t), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);

    init_buffer(rb);

    pthread_t spk_tid, mic_tid;
    pthread_create(&spk_tid, NULL, spk_thread, rb);
    pthread_create(&mic_tid, NULL, mic_thread, rb);

    printf("Main: Started SPK and MIC threads\n");

    pthread_join(spk_tid, NULL);
    pthread_join(mic_tid, NULL);

    pthread_mutex_destroy(&rb->mutex);
    pthread_cond_destroy(&rb->cond);
    munmap(rb, sizeof(ring_buffer_t));
    close(shm_fd);
    shm_unlink(SHM_NAME);

    printf("Main: Exiting program\n");

    return 0;
}

运行结果如下:
在这里插入图片描述

3、通过读写文件实现一个缓冲区

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#define FILE_NAME "queue_file"
#define FILE_SIZE 24
#define WRITE_SIZE 12
#define READ_SIZE 3

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// 写入数据到队列文件
void write_to_queue_file(const char *data, int size, int *write_position) {
    pthread_mutex_lock(&mutex);
    FILE *file = fopen(FILE_NAME, "r+");
    if (file == NULL) {
        perror("Error opening file");
        exit(1);
    }
    // 将文件指针移动到当前写入位置
    fseek(file, *write_position, SEEK_SET);
    // 写入数据
    fwrite(data, 1, size, file);
    *write_position = (*write_position + size) % FILE_SIZE; // 更新写入位置
    fclose(file);
    pthread_mutex_unlock(&mutex);
}

// 从队列文件读取数据
void read_from_queue_file(char *data, int size, int *read_position) {
    pthread_mutex_lock(&mutex);
    FILE *file = fopen(FILE_NAME, "r");
    if (file == NULL) {
        perror("Error opening file");
        exit(1);
    }
    // 将文件指针移动到当前读取位置
    fseek(file, *read_position, SEEK_SET);
    // 读取数据
    fread(data, 1, size, file);
    *read_position = (*read_position + size) % FILE_SIZE; // 更新读取位置
    fclose(file);
    pthread_mutex_unlock(&mutex);
}

// 线程函数:写入数据到队列文件
void *write_thread_function(void *arg) {
    const char *data1 = "111222333444";
    int write_position = 0;
    while (1) {
        write_to_queue_file(data1, WRITE_SIZE, &write_position);
        usleep(1000000);
    }
}

// 线程函数:从队列文件读取数据
void *read_thread_function(void *arg) {
    char read_data[READ_SIZE] = "123";
    int read_position = 0;
    while (1) {
        read_from_queue_file(read_data, READ_SIZE, &read_position);
        printf("Read from queue file: %s\n", read_data);
        usleep(190000);
    }
}

int main() {
    // 创建固定大小的空洞文件
    FILE *file = fopen(FILE_NAME, "w");
    fseek(file, FILE_SIZE - 1, SEEK_SET);
    fputc('\0', file);
    fclose(file);

    // 创建写入线程和读取线程
    pthread_t write_thread, read_thread;
    pthread_create(&write_thread, NULL, write_thread_function, NULL);
    pthread_create(&read_thread, NULL, read_thread_function, NULL);

    // 主线程等待线程结束
    pthread_join(write_thread, NULL);
    pthread_join(read_thread, NULL);

    return 0;
}

在这里插入图片描述

4、支持一写多读的缓冲区

#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>

#define BUFFER_SIZE 4096 // 环形缓冲区大小

typedef struct
{
    unsigned char *buffer; // 缓冲区
    size_t bufferSize;
    size_t writePos;
    size_t *readPos;   // 动态分配读取位置数组
    size_t numReaders; // 读取者数量
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    bool isWritingDone;
} RingBuffer;

typedef struct
{
    RingBuffer *ringBuffer;
    size_t readSize;
    int readerIndex;
    unsigned int waitTimeUs;
    char outputFilename[256];
} ReaderArgs;

void RingBuffer_Init(RingBuffer *ringBuffer, size_t numReaders)
{
    ringBuffer->buffer = (unsigned char *)malloc(BUFFER_SIZE);
    ringBuffer->bufferSize = BUFFER_SIZE;
    ringBuffer->writePos = 0;
    ringBuffer->readPos = (size_t *)malloc(numReaders * sizeof(size_t));
    memset(ringBuffer->readPos, 0, numReaders * sizeof(size_t));
    ringBuffer->numReaders = numReaders;
    pthread_mutex_init(&ringBuffer->mutex, NULL);
    pthread_cond_init(&ringBuffer->cond, NULL);
    ringBuffer->isWritingDone = false;
}

bool RingBuffer_Write(RingBuffer *ringBuffer, const unsigned char *data, size_t dataSize)
{
    pthread_mutex_lock(&ringBuffer->mutex);

    size_t endPos = (ringBuffer->writePos + dataSize) % ringBuffer->bufferSize;
    if (dataSize <= ringBuffer->bufferSize - ringBuffer->writePos)
    {
        memcpy(ringBuffer->buffer + ringBuffer->writePos, data, dataSize);
    }
    else
    {
        size_t firstChunk = ringBuffer->bufferSize - ringBuffer->writePos;
        memcpy(ringBuffer->buffer + ringBuffer->writePos, data, firstChunk);
        memcpy(ringBuffer->buffer, data + firstChunk, dataSize - firstChunk);
    }
    ringBuffer->writePos = endPos;

    pthread_cond_broadcast(&ringBuffer->cond);
    pthread_mutex_unlock(&ringBuffer->mutex);
    return true;
}

size_t RingBuffer_Read(RingBuffer *ringBuffer, unsigned char *outData, size_t maxSize, int readerIndex)
{
    pthread_mutex_lock(&ringBuffer->mutex);

    while (ringBuffer->readPos[readerIndex] == ringBuffer->writePos && !ringBuffer->isWritingDone)
    {
        pthread_cond_wait(&ringBuffer->cond, &ringBuffer->mutex);
    }

    if (ringBuffer->isWritingDone && ringBuffer->readPos[readerIndex] == ringBuffer->writePos)
    {
        pthread_mutex_unlock(&ringBuffer->mutex);
        return 0; // No more data to read.
    }

    size_t availableBytes = (ringBuffer->writePos + ringBuffer->bufferSize - ringBuffer->readPos[readerIndex]) % ringBuffer->bufferSize;
    size_t bytesToRead = (maxSize < availableBytes) ? maxSize : availableBytes;

    if (bytesToRead <= ringBuffer->bufferSize - ringBuffer->readPos[readerIndex])
    {
        memcpy(outData, ringBuffer->buffer + ringBuffer->readPos[readerIndex], bytesToRead);
    }
    else
    {
        size_t firstChunk = ringBuffer->bufferSize - ringBuffer->readPos[readerIndex];
        memcpy(outData, ringBuffer->buffer + ringBuffer->readPos[readerIndex], firstChunk);
        memcpy(outData + firstChunk, ringBuffer->buffer, bytesToRead - firstChunk);
    }
    ringBuffer->readPos[readerIndex] = (ringBuffer->readPos[readerIndex] + bytesToRead) % ringBuffer->bufferSize;

    pthread_mutex_unlock(&ringBuffer->mutex);
    return bytesToRead;
}

void RingBuffer_Destroy(RingBuffer *ringBuffer)
{
    if (ringBuffer->buffer != NULL)
    {
        free(ringBuffer->buffer);
    }
    pthread_mutex_destroy(&ringBuffer->mutex);
    pthread_cond_destroy(&ringBuffer->cond);
}

void *writer_thread(void *arg)
{
    RingBuffer *ringBuffer = (RingBuffer *)arg;
    FILE *file = fopen("audio.pcm", "rb");
    if (!file)
    {
        printf("Unable to open PCM file for reading.\n");
        return NULL;
    }

    unsigned char data[960];
    while (fread(data, 1, sizeof(data), file) == sizeof(data))
    {
        RingBuffer_Write(ringBuffer, data, sizeof(data));
        usleep(128000); // wait for 128ms
    }

    fclose(file);
    ringBuffer->isWritingDone = true;
    pthread_cond_broadcast(&ringBuffer->cond); // Notify all waiting readers
    return NULL;
}

void *reader_thread(void *arg)
{
    ReaderArgs *rargs = (ReaderArgs *)arg;

    FILE *outputFile = fopen(rargs->outputFilename, "wb");
    if (!outputFile)
    {
        printf("Unable to open PCM file for writing: %s\n", rargs->outputFilename);
        return NULL;
    }

    unsigned char *data = (unsigned char *)malloc(rargs->readSize);
    size_t bytesRead;
    while ((bytesRead = RingBuffer_Read(rargs->ringBuffer, data, rargs->readSize, rargs->readerIndex)) > 0 || !rargs->ringBuffer->isWritingDone)
    {
        fwrite(data, 1, bytesRead, outputFile);
        usleep(rargs->waitTimeUs); // wait for specified time
    }

    free(data);
    fclose(outputFile);
    return NULL;
}

int main()
{
    RingBuffer ringBuffer;
    size_t numReaders = 2;
    RingBuffer_Init(&ringBuffer, numReaders);

    pthread_t writer;
    pthread_create(&writer, NULL, writer_thread, &ringBuffer);

    ReaderArgs rargs1 = {&ringBuffer, 480, 0, 64000, "output1.pcm"};
    ReaderArgs rargs2 = {&ringBuffer, 128, 1, 10000, "output2.pcm"};
    pthread_t reader1, reader2;
    pthread_create(&reader1, NULL, reader_thread, &rargs1);
    pthread_create(&reader2, NULL, reader_thread, &rargs2);

    pthread_join(writer, NULL);
    pthread_join(reader1, NULL);
    pthread_join(reader2, NULL);

    RingBuffer_Destroy(&ringBuffer);

    return 0;
}

这个缓冲区支持一写多读,测试程序创建了两个读线程一个写线程,读线程读取一个pcm文件,每隔128ms写入缓冲区,每次写入960个字节的数据,读线程有两个,一个每次读取480个字节的数据,读取间隔为64ms,一个每次读取128字节的数据,读取间隔为10ms。最终读取效果如下所示:
在这里插入图片描述

5、另一种支持一读多写的缓冲区

test.c

#include "ringbuf.h"
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
circular_buffer cb_global;

void *writer_thread(void *arg)
{
    circular_buffer *cb = (circular_buffer *)arg;
    char input_data[1024] = {0};
    while (1)
    {
        if (cb_write(cb, input_data, 1024) == 1024)
        {
            printf("write over \n");
        }
        else
        {
            printf("write error \n");
        }
        sleep(1);
    }
    return NULL;
}

void *reader_thread1(void *arg)
{
    int reader_id = *(int *)arg;
    circular_buffer *cb = &cb_global;
    char data[512];
    while (1)
    {
        int bytes_read = cb_read(cb, data, sizeof(data), reader_id, 960);
        if (bytes_read > 0)
        {
            printf("Reader %d read %d\n", reader_id, bytes_read);
        }
        sleep(0.5);
    }
    return NULL;
}

void *reader_thread2(void *arg)
{
    int reader_id = *(int *)arg;
    circular_buffer *cb = &cb_global;
    char data[256];
    while (1)
    {
        int bytes_read = cb_read(cb, data, sizeof(data), reader_id, 256);
        if (bytes_read > 0)
        {
            printf("Reader %d read %d\n", reader_id, bytes_read);
        }
        sleep(0.25);
    }
    return NULL;
}

int main()
{
    cb_init(&cb_global, 4096);

    pthread_t writer, reader1, reader2;
    int reader1_id = cb_register_reader(&cb_global, 512);
    int reader2_id = cb_register_reader(&cb_global, 256);

    pthread_create(&writer, NULL, writer_thread, (void *)&cb_global);
    pthread_create(&reader1, NULL, reader_thread1, (void *)&reader1_id);
    pthread_create(&reader2, NULL, reader_thread2, (void *)&reader2_id);

    pthread_join(writer, NULL);
    pthread_join(reader1, NULL);
    pthread_join(reader2, NULL);

    cb_free(&cb_global);
    return 0;
}

ringbuf.c

#include "ringbuf.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int cb_init(circular_buffer *cb, int size)
{
    cb->size = size;
    cb->buffer = (char *)malloc(size);
    if (!cb->buffer)
        return -1;

    cb->write_pos = 0;
    cb->reader_count = 0;
    memset(cb->read_pos, 0, sizeof(cb->read_pos));
    memset(cb->read_size, 0, sizeof(cb->read_size));

    pthread_mutex_init(&cb->mutex, NULL);
    pthread_cond_init(&cb->can_write, NULL);

    for (int i = 0; i < MAX_READERS; i++)
    {
        cb->can_read[i] = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
    }

    return 0;
}

void cb_free(circular_buffer *cb)
{
    free(cb->buffer);
    pthread_mutex_destroy(&cb->mutex);
    pthread_cond_destroy(&cb->can_write);
    for (int i = 0; i < MAX_READERS; i++)
    {
        pthread_cond_destroy(&cb->can_read[i]);
    }
}

int cb_write(circular_buffer *cb, const char *data, int size)
{
    pthread_mutex_lock(&cb->mutex);

    int written = 0;
    while (written < size)
    {
        int next_pos = (cb->write_pos + 1) % cb->size;
        int can_write = 1;
        for (int i = 0; i < cb->reader_count; i++)
        {
            if (next_pos == cb->read_pos[i])
            {
                can_write = 0;
                break;
            }
        }

        if (can_write)
        {
            cb->buffer[cb->write_pos] = data[written++];
            cb->write_pos = next_pos;
        }
        else
        {
            pthread_cond_wait(&cb->can_write, &cb->mutex);
        }
    }

    for (int i = 0; i < cb->reader_count; i++)
    {
        pthread_cond_signal(&cb->can_read[i]);
    }

    pthread_mutex_unlock(&cb->mutex);
    return written;
}

int cb_read(circular_buffer *cb, char *data, int size, int reader_id, int blocking)
{
    pthread_mutex_lock(&cb->mutex);

    int read = 0;
    while (read < size)
    {
        int can_read = (cb->write_pos + cb->size - cb->read_pos[reader_id]) % cb->size >= cb->read_size[reader_id];
        if (can_read)
        {
            data[read++] = cb->buffer[cb->read_pos[reader_id]];
            cb->read_pos[reader_id] = (cb->read_pos[reader_id] + 1) % cb->size;
        }
        else if (blocking)
        {
            pthread_cond_wait(&cb->can_read[reader_id], &cb->mutex);
        }
        else
        {
            break;
        }
    }

    if (read > 0)
    {
        pthread_cond_signal(&cb->can_write);
    }

    pthread_mutex_unlock(&cb->mutex);
    return read;
}

int cb_register_reader(circular_buffer *cb, int read_size)
{
    pthread_mutex_lock(&cb->mutex);
    int reader_id = cb->reader_count;
    if (reader_id < MAX_READERS)
    {
        cb->read_size[reader_id] = read_size;
        cb->read_pos[reader_id] = cb->write_pos;
        cb->reader_count++;
    }
    else
    {
        reader_id = -1;
    }
    pthread_mutex_unlock(&cb->mutex);
    return reader_id;
}

ringbuf.h

#ifndef RINGBUF_H
#define RINGBUF_H

#include <pthread.h>

#define MAX_READERS 3 // 最大读线程数

typedef struct
{
    char *buffer;
    int size;
    int write_pos;
    int read_pos[MAX_READERS];
    int read_size[MAX_READERS];
    int reader_count;
    pthread_mutex_t mutex;
    pthread_cond_t can_write;
    pthread_cond_t can_read[MAX_READERS];
} circular_buffer;

int cb_init(circular_buffer *cb, int size);
void cb_free(circular_buffer *cb);
int cb_write(circular_buffer *cb, const char *data, int size);
int cb_read(circular_buffer *cb, char *data, int size, int reader_id, int blocking);
int cb_register_reader(circular_buffer *cb, int read_size);

#endif // RINGBUF_H

最终效果如下所示:
在这里插入图片描述
总结来看,支持一读多写的缓冲区其实就是设计了两个读的游标,然后写的部分写完后广播给其他进程来读取数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

桃成蹊2.0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值