1.消费者比生产者速度快,等待生产者写入;
2.生产者比消费者快,则最新数据覆盖最先写入数据;
3.实际运用时,根据需求修改缓冲区数据类型。
//环形存取缓冲区
public class RingBuffer
{
//字段
/// <summary>
/// 内部缓冲区
/// </summary>
private readonly double[] _buffer;
/// <summary>
/// 内部缓冲区容量
/// </summary>
private readonly int _nSize;
/// <summary>
/// 出队指针(队头)
/// </summary>
private int _nHead;
/// <summary>
/// 入队指针(队尾)
/// </summary>
private int _nTail;
//属性
/// <summary>
/// 获取环形队列包含元素数量
/// </summary>
public int Count
{
get
{
if (_nTail == _nHead) return 0;
return _nTail > _nHead ? _nTail - _nHead : _nTail + _nSize - _nHead;
}
}
/// <summary>
/// 获取环形队列队头索引
/// </summary>
public int HeadIndex { get { return _nHead; } }
/// <summary>
/// 获取环形队列队尾索引
/// </summary>
public int TailIndex { get { return _nTail; } }
//构造函数
/// <summary>
/// 设置缓冲区最大容量
/// </summary>
/// <param name="nMaxLen">缓冲区最大容量</param>
public RingBuffer(int nMaxLen)
{
_nSize = nMaxLen;
_nTail = 0;
_nHead = 0;
_buffer = new double[nMaxLen];
}
//方法
/// <summary>
/// 将对象添加到队列结尾处
/// </summary>
/// <param name="nData">入队数据</param>
public void Enqueue(double nData)
{
lock (_buffer)
{
_buffer[_nTail] = nData;
_nTail = (_nTail + 1) % _nSize;
if (_nTail == _nHead) _nHead = (_nHead + 1) % _nSize;//队尾追着队头跑
}
}
/// <summary>
/// 移除并返回队头数据
/// </summary>
public double Dequeue()
{
if (_nTail == _nHead) throw new InvalidOperationException("队列为空");//队列中无数据,抛出错误
lock (_buffer)
{
double dDequeue = _buffer[_nHead];//获取队头数据
_nHead = (_nHead + 1) % _nSize;
return dDequeue;//返回队头数据
}
}
/// <summary>
/// 清空缓冲区,但不释放缓冲区空间
/// </summary>
public void Clear()
{
lock (this)
{
_nTail = 0;
_nHead = 0;
}
}
/// <summary>
/// 获取队头数据,但不移除数据
/// </summary>
public double Peek()
{
if (_nTail == _nHead) throw new InvalidOperationException("队列为空");//队列中无数据,抛出错误
lock (_buffer)
{
return _buffer[_nHead];
}
}
/// <summary>
/// 获取队尾数据,但不移除数据
/// </summary>
public double Tail()
{
if (_nTail == _nHead) throw new InvalidOperationException("队列为空");//队列中无数据,抛出错误
if (_nTail == 0) return _buffer[_nSize - 1];
return _buffer[_nTail - 1];
}
/// <summary>
/// 根据(逻辑)索引获取物理索引对应的数据
/// </summary>
/// <param name="nDataIndex">(逻辑)索引值</param>
public double GetDataByIndex(int nDataIndex)
{
int pos = GetPosByIndex(nDataIndex);
return GetDataByPos(pos);
}
/// <summary>
/// 根据逻辑索引获取物理索引
/// </summary>
/// <param name="nIndex">逻辑索引</param>
/// <returns>物理索引</returns>
public int GetPosByIndex(int nIndex)
{
if (nIndex >= _nSize) throw new InvalidOperationException("非法索引");
if (_nTail == _nHead) throw new InvalidOperationException("队列为空");//队列中无数据,抛出错误
int pos = _nHead + nIndex;//逻辑索引获取物理索引
if (pos >= _nSize) pos -= _nSize;//若获取到的物理索引值大于缓冲区最大值,需设置物理索引为正确索引值
if (IsExistsThePos(pos)) return pos;
throw new InvalidOperationException("指定索引处超出范围");
}
/// <summary>
/// 获取缓冲区所对应物理索引的值
/// </summary>
/// <param name="nPos">物理索引</param>
public double GetDataByPos(int nPos)
{
if (IsExistsThePos(nPos)) return _buffer[nPos];
throw new InvalidOperationException("指定索引超出范围");
}
/// <summary>
/// 判断物理索引是否存在
/// </summary>
/// <param name="nPos">物理索引</param>
public bool IsExistsThePos(int nPos)
{
if (nPos >= _nSize || nPos < 0 || _nHead == _nTail) return false;
//1.队尾大于队头,物理索引须位于队头和队尾之间
if (_nTail > _nHead) return nPos >= _nHead && nPos < _nTail;
//2.队尾小于队头,物理索引不能位于队头和队尾之间
return nPos < _nTail || nPos >= _nHead;
}
/// <summary>
///
/// 获取缓冲区所有数据
/// </summary>
/// <returns>返回数据数组</returns>
public double[] GetAllData()
{
if (_nTail == _nHead) return null; //队列中无数据,返回null
lock (_buffer)
{
var tList = new List<double>();
if (_nHead < _nTail)
{
//队头小于队尾时,从队头取到队尾
for (int i = _nHead; i < _nTail; i++) tList.Add(_buffer[i]);
}
else
{
//队头大于队尾时,需分两步获取数据
//1.从队头数据开始取数据,一直到队列中逻辑上最后一个元素
for (int i = _nHead; i < _nSize; i++)
tList.Add(_buffer[i]);
//2.从队列逻辑上第一个元素,取到队尾
for (int i = 0; i < _nTail; i++)
tList.Add(_buffer[i]);
}
return tList.ToArray();
}
}
}