MSMQ 支持的 FIFO 队列






4.50/5 (6投票s)
一个标准的内存通用 .NET 队列,当可配置的溢出限制达到时,将开始使用 MSMQ 作为后端,以减轻内存使用问题。
引言
我最近遇到了一个问题,需要将一组对象存储在队列中以便快速访问。当开始在内存中存储大量这些对象时,这成了一个问题,这些对象本身具有相当大的大小。我遇到了OutOfMemoryException
和其他由于系统内存过载导致的奇怪行为。
OverFlowQueue<T>
是需要一个 FIFO 队列的直接结果,该队列在被对象淹没时不会耗尽我的系统内存。 使用 MSMQ 作为标准 .NET 通用队列的后端成功地缓解了内存问题,并且性能下降可以接受。
话虽如此,我确信我可以对这个类进行改进,并且我欢迎所有反馈!
Using the Code
这里包含的示例项目中有几个类,我只讨论OverflowQueue<T>
,但请随意使用其他类,这些类包含创建/修改 MSMQ 队列、检索类型化 app.config 值和其他实用程序方法的方法。
OverflowQueue<T>
类本身必须使用可序列化类型创建。 这是由用于打包和解包 MSMQ 中项目的二进制格式化程序强制执行的。
OverflowQueue<T>
有一个构造函数,它接受两个参数
public OverflowQueue(string queueName, bool useBackgroundMsmqPull)
{...}
第一个参数,queueName
,是应该创建并用于从内存队列溢出的 MSMQ 队列的名称。
第二个参数,useBackgroundMsmqPull
,定义了从 MSMQ 中提取以重新填充内存队列的操作是在调用线程上(在调用 Dequeue()
时)还是在每秒运行的后台线程上完成。 为了性能原因,我将此值设置为 true
,以便从 MSMQ 中提取发生在辅助线程上,并且不会延迟我的出队操作。 从 MSMQ 中提取的操作如下
void PullFromMSMQ()
{
// We've been putting all new messages into MSMQ...
// Now is the time to get them out and put them back into memory.
while (Interlocked.Read(ref currentMSMQSize) > 0
&& Interlocked.Read(ref currentQueueSize) < maxInternalQueueSize)
{
Message message = overflowMSMQ.Receive();
// decrement the MSMQ size
Interlocked.Decrement(ref currentMSMQSize);
T item = message.Body as T;
PushToMemoryQueue(item);
}
if (Interlocked.Read(ref currentMSMQSize) <= 0)
{
// lock to prevent incorrect count when turning off msmq pushing
lock (msmqLock)
{
if (Interlocked.Read(ref currentMSMQSize) <= 0)
pushingToMSMQ = false;
}
}
}
因此,您可以看到我为什么使用后台线程从 MSMQ 中提取……我的应用程序在出队方面有很多处理,因此让后台线程从 MSMQ 中提取对我来说更好,但这可能不适用于所有人。
OverflowQueue<T>
类的其余部分使用起来相当容易; 有以下函数允许您对消息进行入队或出队
public void Enqueue(T item) {...}
public T Dequeue() {...}
这两个函数都将透明地使用溢出 MSMQ 队列来对消息进行入队或出队,如果您下载源代码,您可以看到这一点。
关注点
我创建这个类的目标是创建一个实用程序,该实用程序透明地将 MSMQ 后端用于我的队列。 所有与创建和使用底层 MSMQ 相关的函数都封装在 OverflowQueue<T>
类本身中。
使用此类只有两个先决条件
- 用户必须安装 MSMQ
OverflowQueue<T>
中的类型<T>
必须是可序列化的
历史
这是 OverflowQueue<T>
类的第一个版本。