一:概述
实际学习和工作中,我们经常会遇到读写大量数据的情况,这个时候我们可能就用到了循环缓冲区。
循环缓冲区在处理大量数据的时候有很大的优点,循环缓冲区在一些竞争问题上提供了一种免锁的机制,免锁的前提是,生产者和消费都只有一个的情况下,否则也要加锁。
二:循环缓冲区的实现理论如下图
三:实现代码如下所示:
//CRecycleQueue.h #include<iostream> //循环缓冲区类模板 template<class T> class CRecycleQueue{ private: //循环缓冲区地址指针 T **_queue; //循环缓冲区读游标 (读的位置) int _read; //循环缓冲区写游标 (写的位置) int _write; //循环缓冲区的大小 int _size; //我们姑且称这个变量为掩码,接下来用来作位&运算,从而实现循环缓冲 int _mask; public: CRecycleQueue(){ _queue = NULL; _read = 0; _write = 0; _size = 0; _mask = 0; } //初始化循环缓冲区 bool InitRecycleQueue(int exp){ if(0 > exp){ return false; } _read = 0; _write = 0; //传进来一个整数,对1进行位移操作 //比如exp = 4 //_size的二进制表示:1000 _size = 1 << exp; //_mask的二进制表示:0111 _mask = _size - 1; //分配缓冲区空间 _queue = (T **)new char[sizeof (T *) * _size]; if(NULL == _queue){ return false; } return true; } /* * size = 1000 mask = 0111 * write或read同mask 作&运算,可以实现循环缓冲区的功能 * 也许你会问这里为什么不使用 % 运算实现循环的循环功能呢? * 答案是系统 & 运算效率要比 % 运算效率高 * * Push: * write = 0; * 0000 & 0111 = 0; write++ (写入缓冲队列的第0位置) * write = 1; * 0001 & 0111 = 1; write++ (写入缓冲队列的第1位置) * write = 2; * 0010 & 0111 = 2; write++ * write = 3; * 0011 & 0111 = 3; write++ * ... * write = 8; * 1000 & 0111 = 0; write++ * write = 9; * 1001 & 0111 = 1; write++ * ... * * Pop: * read = 0; * 0000 & 0111 = 0; read++ (读取缓冲队列的第0位置的数据) * read = 1; * 0001 & 0111 = 1; read++ (读取缓冲队列的第1位置的数据) * read = 2; * 0010 & 0111 = 2; read++ * read = 3 * 0011 & 0111 = 3; read++ * ... * read = 8; * 1000 & 0111 = 0; read++ * ... * */ bool Push(T *type){ if(NULL == type){ return false; } //当条件不满足的时候,说明缓冲区已满,Push进来的数据就会丢失 if(_write < _read + _size){ //我们这里存入的是type指针,这个指针指向了一个我们分配的内存空间或者类 _queue[_write & _mask] = type; _write++; return true; } return false; } T *Pop(){ T *tmp = NULL; //当条件不满足的时候说明缓冲区已经没有数据 if(_read < _write){ //取出队列的数据 tmp = _queue[_read & _mask]; _read++; } return tmp; } int GetRemainSize(){ return (_write - _read); } };
下面是简单的测试代码:
//main.cpp #include <iostream> #include <pthread.h> #include "CRecycleQueue.h" using namespace std; class UserInfo{ private : int _num; public: UserInfo(int num){ _num = num; } int getUserNum(){ return _num; } }; CRecycleQueue<UserInfo> *queue = new CRecycleQueue<UserInfo>; void *write_func(void *args){ int num = 0; while(1){ //UserInfo里可以封装你自己想要的数据 //这里仅仅是一个简单的测试用例 UserInfo *info = new UserInfo(num++); if(!queue->Push(info)){ //Push失败 删除手动分配的内存空间 delete info; } sleep(1); } } void *read_func(void *args){ while(1){ UserInfo *info = NULL; if(info = queue->Pop()){ cout<<info->getUserNum()<<endl; delete info; } sleep(1); } } int main(){ queue->InitRecycleQueue(8); pthread_t pid1; pthread_t pid2; //这种生产者和消费者都只有一个的情况下,这个循环缓冲区为竞争问题提供了免锁,大大提高了程序的处理效率 pthread_create(&pid1,NULL,read_func,NULL); pthread_create(&pid2,NULL,write_func,NULL); pthread_join(pid1,NULL); pthread_join(pid2,NULL); return 0; }
编译:g++ main.cpp -lpthread -o test
这个循环缓冲队列大体的功能已经实现,其中循环缓冲队列一些其他操作并没有去实现,只是描述了一些核心的操作!
如果有错误和其他意见,提出来大家一起相互讨论和学习!