C语言实现线程安全环形缓冲区详解

Owen
Owen
发布时间:2026-10-13 | 5人浏览过
原创

在多线程环境下高效管理数据流时,环形缓冲区(Circular Buffer)是解决生产者-消费者模型的关键数据结构。本文将深入解析使用 C 语言实现的一个线程安全、基于固定大小数组且支持快速读写操作的 FIFO 缓冲区方案。

环形缓冲区的核心设计逻辑

环形缓冲区通过维护两个指针——写指针(in)和读指针(out),结合一块连续分配的内存空间来实现循环复用。其核心难点在于处理指针越界(回绕)以及并发访问时的数据竞争问题。本方案采用以下策略:

  1. 容量限制:缓冲区大小(BUFFSIZE)必须为 2 的幂次方,例如 1024 * 1024。这使得可以通过位运算(& (fifo->size - 1))替代昂贵的除法取模运算,大幅提升性能。
  2. 状态判断:in 和 out 是单调递增的无符号整数,并不回绕。可用数据量为 in - out;已占用空间可通过公式计算,空余空间为 size - in + out。
  3. 线程安全:引入 pthread_mutex_t 互斥锁,确保在同一时刻只有一个线程能修改指针或写入/读取数据,防止脏读和内存损坏。

完整代码实现

以下为包含初始化、读写核心逻辑及多线程测试用例的完整 C 语言代码。请注意预处理文件导入部分需根据实际运行环境补全标准库头文件。

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

#define BUFFSIZE 1024 * 1024 
#define min(x, y) ((x) < (y) ? (x) : (y)) 

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 

struct cycle_buffer { 
 unsigned char *buf; 
 unsigned int size; 
 unsigned int in; 
 unsigned int out; 
 pthread_mutex_t lock; 
}; 

static struct cycle_buffer *fifo = NULL; 

// 初始化环形缓冲区
static int init_cycle_buffer(void) 
{
 int size = BUFFSIZE, ret; 
 // 校验 size 是否为 2 的幂
 ret = size & (size - 1); 
 if (ret) 
  return ret;
 fifo = (struct cycle_buffer *) malloc(sizeof(struct cycle_buffer)); 
 if (!fifo) 
  return -1; 

 memset(fifo, 0, sizeof(struct cycle_buffer)); 
 fifo->size = size; 
 fifo->in = fifo->out = 0; 
 pthread_mutex_init(&fifo->lock, NULL); 
 fifo->buf = (unsigned char *) malloc(size); 
 if (!fifo->buf)
  free(fifo);
 else
  memset(fifo->buf, 0, size); 
 return 0; 
}

// 从缓冲区读取数据
unsigned int fifo_get(unsigned char *buf, unsigned int len) 
{ 
 unsigned int l; 
 // 计算可读字节数:剩余总数据与请求长度的较小值
 len = min(len, fifo->in - fifo->out); 
 // 分块复制技巧:第一块写到 buffer 末尾,第二块从头开始
 l = min(len, fifo->size - (fifo->out & (fifo->size - 1))); 
 memcpy(buf, fifo->buf + (fifo->out & (fifo->size - 1)), l); 
 memcpy(buf + l, fifo->buf, len - l); 
 fifo->out += len; 
 return len; 
}

// 向缓冲区写入数据
unsigned int fifo_put(unsigned char *buf, unsigned int len) 
{ 
 unsigned int l; 
 // 计算可写字节数:剩余空间与请求长度的较小值
 len = min(len, fifo->size - fifo->in + fifo->out); 
 // 分块写入技巧
 l = min(len, fifo->size - (fifo->in & (fifo->size - 1))); 
 memcpy(fifo->buf + (fifo->in & (fifo->size - 1)), buf, l); 
 memcpy(fifo->buf, buf + l, len - l); 
 fifo->in += len; 
 return len; 
}

// 读取线程函数
static void * thread_read(void *arg) 
{ 
 char buf[1024]; 
 unsigned int n; 
 pthread_detach(pthread_self()); 
 for (;;) { 
  memset(buf, 0, sizeof(buf)); 
  pthread_mutex_lock(&fifo->lock); 
  n = fifo_get(buf, sizeof(buf)); 
  pthread_mutex_unlock(&fifo->lock); 
  write(STDOUT_FILENO, buf, n); 
 }
 printf("nnafter thread_read : %snn",buf);
 return NULL; 
}

// 写入线程函数
static void * thread_write(void *arg) 
{ 
 unsigned char buf[] = "hello world"; 
 pthread_detach(pthread_self()); 
 for (;;) { 
  pthread_mutex_lock(&fifo->lock); 
  fifo_put(buf, strlen(buf)); 
  pthread_mutex_unlock(&fifo->lock); 
 } 
 return NULL; 
}

int main(void) 
{ 
 int ret; 
 pthread_t wtid, rtid; 
 ret = init_cycle_buffer(); 
 if (ret == -1) 
  return ret;

 pthread_create(&wtid, NULL, thread_write, NULL); 
 pthread_create(&rtid, NULL, thread_read, NULL); 
 pthread_exit(NULL); 
 return 0;
}

关键步骤与原理解析

第1步:内存布局与指针机制

在进行初始化和结构体定义时,buf 指向物理存储区,size 记录总大小,in 代表下一个待写入的位置索引,out 代表下一个待读取的位置索引。为了保证效率,size 被强制设定为 2 的幂次方。这样,(index & (size - 1)) 等价于 index % size,但避免了耗时的除法和取模运算。

第2步:环形切割复制(Wrap-around Copy)

由于数据可能在缓冲区末端断裂(即一部分在末尾,另一部分回到头部),直接单次 memcpy 会导致溢出。算法采用“切开”策略:

  • 写入时:首先尽可能填满 in 到 size-1 之间的区域。如果数据量超过该区域剩余空间,剩余的字节将从数组头部 0 处继续写入。
  • 读取时:同理,先从 out 读到数组末尾,若需读取更多,则紧接着从数组头部继续读取。

第3步:并发控制与防死锁

在 thread_write 和 thread_read 中,任何对共享内存的操作都必须包裹在 pthread_mutex_lock 和 pthread_mutex_unlock 之间。这确保了当写线程正在更新 in 指针并填充数据时,读线程不会尝试读取未完全写入的数据,反之亦然。最后通过 pthread_detach 分离线程资源,使主线程无需显式等待即可退出。

排错与维护提示

  • Size 校验失败:如果在 init_cycle_buffer 中 ret 不为 0,说明传入的缓冲区大小不是 2 的幂。这会破坏位掩码取模的逻辑,导致后续读写越界。
  • 无符号下溢:代码中使用无符号整型计算 len 和指针差值。如果逻辑错误导致读指针超过写指针(在没有重置的情况下),会引发巨大的数值差异,因此必须严格依赖互斥锁保护一致性。

以上就是C语言中实现环形缓冲区的详细内容,更多关于C语言并发编程与数据结构设计的资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表