目标
先预出队一个成员,使用过程中如果出现问题再撤销出队,比如转发一条消息但没有成功,此时消息应该仍然留在队列中,下次重新发送。
现有的Queue<T> Dequeue后不能在队列头部插入一条,即不能撤销出队操作。
队列数据结构
队列是一个单链表结构,即通过不断的Next延长队列长度,队列应该有头和尾,出队时从头出,入队时从尾入。
队列节点定义如下:
class QNode<T>
{
public QNode(T data)
{
Data = data;
}
public T Data { get; set; }
public QNode<T> Next { get; set; }
}
出队内存的重用
每个队列需要占用2个指针的内存,出队后的QNode就不会再用了,下次入队还要再创建新的QNode,如果能回收出队的QNode用作入队,可以减少内存开销。
回收队列不需要太复杂的逻辑,只需把不用的QNode入队,用时再出队就行了,队列耗尽时返回null,调用者可以new一个新QNode。
class QNodeRecycle<T>
{
private QNode<T> head, tail;
public QNodeRecycle()
{
head = new QNode<T>(default(T));
tail = head;
}
public void Enqueue(QNode<T> node)
{
tail.Next = node;
tail = tail.Next;
}
public QNode<T> Dequeue()
{
if(head.Next != null)
{
var result = head;
head = head.Next;
result.Next = null;
return result;
}
return null;
}
}
可撤销队列代码
关键是Dequeue里面传了一个 Func<T,bool>处理函数,当处理函数返回false时将出队撤销,也提供了不可撤销的普通出队。
/// <summary>
/// 可撤销出队的队列.
/// </summary>
public class CancelableQueue<T>
{
private QNode<T> head = null, tail = null;
QNodeRecycle<T> recycle;//用于存储出队后废弃的成员,减少内存分配.
public CancelableQueue()
{
recycle = new QNodeRecycle<T>();
}
public int Count { get; private set; }
public void Enqueue(T t)
{
QNode<T> node = recycle.Dequeue();
if (node == null)
{
node = new QNode<T>(t);
}
else
{
node.Data = t;
}
if(head == null)
{
head = node;
tail = node;
}//第一条
else
{
tail.Next = node;
tail = tail.Next;
}
Count++;
}
public void Dequeue(Func<T,bool> func)
{
if (Count > 0)
{
var result = head;
head = head.Next;
result.Next = null;
if(func(result.Data))
{
recycle.Enqueue(result);
Count--;
}
else
{
result.Next = head;
head = result;
}//出队失败,恢复head.
}
}
public T Dequeue()
{
if (Count > 0)
{
var result = head;
head = head.Next;
result.Next = null;
recycle.Enqueue(result);
Count--;
return result.Data;
}
return default;
}
}