MessageQueue不止一次处理

时间:2012-03-20 21:09:39

标签: c# message-queue dispose

我在其他帖子上看到过此错误,但不是出于这种情况。

我有两个类使用MessageQueue做同样的事情。因此,我将队列的创建和处理抽象为helper类。我收到了这个错误,我无法看到如何多次处理队列。

对象'messageQueue'可以在方法'MsmqHelper.DisposeQueue(MessageQueue)'中多次处理

在其中一个类中,这是队列的使用方式:

private MessageQueue _messageQueue;

然后,在类的构造函数中:

this._messageQueue = MsmqHelper.InitializeQueue();

这并不重要,但为了完整性,这里是使用队列的地方:

this._messageQueue.Send(workflowCreated);

以下是Dispose方法:

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

private void Dispose(bool disposing)
{
    if (disposing == false) { return; }

    MsmqHelper.DisposeQueue(this._messageQueue);
}

这是helper类中实际调用Dispose()的代码:

public static void DisposeQueue(MessageQueue messageQueue)
{
    if (messageQueue != null)
    {
        messageQueue.Close();
        messageQueue.Dispose();
        messageQueue = null;
    }
}

在这种情况下,队列可以在多长时间内被丢弃?

**编辑**

我认为在下面的对话中添加我的评论会很高兴。这是一个很好的总结,以及接受的答案:

我想我现在明白了。 messageQueue方法参数与对象的原始(this._messageQueue)引用无关。因此,检查messageQueue为null,并将其设置为null,没有任何好处。即使在被处理之后,调用者仍然可以传入其变量(this._messageQueue)。因此,能够不止一次处置。

顺便说一句,即使在调用方法中将调用者的变量(this._messageQueue)设置为null也无济于事。该问题仅存在于MsmqHelper.DisposeQueue()中。所以答案是通过ref传递或者只是不调用DisposeQueue()并在调用方法中完成所有操作。

**编辑2 **

尝试此操作后,我得到了同样的错误。我根本就没有得到它。

public static void DisposeQueue(ref MessageQueue messageQueue)
{
    if (messageQueue == null) { return; }

    messageQueue.Close();
    messageQueue.Dispose();
    messageQueue = null;
}

**编辑3 - 错误? **

我开始认为这可能是一个错误。如果我评论messageQueue.Dispose(),则错误消失。但是,我可以调用方法中一起调用messageQueue.Close()和messageQueue.Dispose()。去搞清楚。我想我只是从调用方法调用这些调用,或者只调用Close()或Dispose()而不是两者。

3 个答案:

答案 0 :(得分:2)

是。这可以多次配置对象:

调用this._messageQueue后,MsmqHelper.DisposeQueue(this._messageQueue)评估的值会发生变化。

messageQueue方法中仅为本地参数(名为null)分配了值DisposeQueue。因此,“无效防守”无法正确保护后续时间。 (这是因为C#的默认行为是Call-By-Value:请参阅链接以了解在“传递对象的引用值”的上下文中这意味着什么。)

接听ref或在来电者中分配this._messageQueue = null

答案 1 :(得分:2)

Close释放MessageQueue对象的所有资源。请参阅documentation here。该错误很可能是在CA中生成的,因为它看到Close的执行路径也调用Dispose。

来自文档:

    public  void ReceiveMessage()
    {
        // Connect to the a on the local computer.
        MessageQueue myQueue = new MessageQueue(".\\myQueue");

        // Set the formatter to indicate body contains an Order.
        myQueue.Formatter = new XmlMessageFormatter(new Type[]
            {typeof(String)});

        try
        {
            // Receive and format the message. 
            Message myMessage1 = myQueue.Receive();
            Message myMessage2 = myQueue.Receive();
        }

        catch (MessageQueueException)
        {
            // Handle sources of any MessageQueueException.
        }

        // Catch other exceptions as necessary.

        finally
        {
            // Free resources.
            myQueue.Close();
        }

        return;
    }

关闭显然会释放资源,但如果尚未收集,则允许组件重新获取它们。打开MessageQueue对象,使用它然后在同一个调用中关闭它而不是打开它一段时间并稍后关闭它可能更为谨慎,因为连接缓存消除了在重复调用中打开MessageQueue的开销。 / p>

*的 更新 * 看起来CA对于成员字段对待CA2202与将一次性对象传递给方法的方式不同,即使该方法对于类是私有的。无论如何,根据文档,您只需要调用Close()或Dispose(),但不能同时调用两者。我建议您更改设计,以便在消息操作的范围内创建,使用然后关闭MessageQueue对象,如上面演示的文档示例中的示例。

答案 2 :(得分:1)

如果 MessageQueue 类实现了IDisposable iterface,那么没有必要明确使用Dispose方法和Close()方法,因为在所有这类中,Close()方法通常都不是一个iterface方法,而是一个类方法。通常,在Dispose方法中,所有正确的命令都应该在释放托管/非托管资源之前调用Close()方法。

同样,通过强制使用外部静态助手,可以打破Disposable模式。这不是控制对象生命周期的正确方法;你不需要搞乱Disposable模式,你可以简单地使用它

您的代码可能会像这样简化:

    // 1. Use static class. By the agreement, all helper classes should be static to avoid 
    // IDisposable inheritance, in example
    public static class MsmqHelper//: IDisposable
    {
        //private MessageQueue _messageQueue;

        //public MessageQueueHelper(bool workflowCreated)
        //{
        //    this._messageQueue = MsmqHelper.InitializeQueue();
        //    this._messageQueue.Send(workflowCreated);
        //}

        public static SendMessage(object workflowCreated)
        {
            // 2. If static method in static class does not takes parameters, 
            // I might be better to to implicitly call the constructor?

            // using(MessageQueue msmsq = MsmqHelper.InitializeQueue())

            using(MessageQueue msmsq = new MessageQueue())
            {
                msmq.Send(workflowCreated);
                msmq.Close(); 

                // MsmqHelper.DisposeQueue(msmq);

                // 3. You should explicitly call Close object to immediately release     
                // unmanaged resources, while managed ones will be released 
                // at next GC rounds, as soon as possible
            }
        }
        //private MessageQueue _messageQueue;

        //public void Dispose()
        //{
        //    Dispose(true);
        //    GC.SuppressFinalize(this);
        //}

        //private void Dispose(bool disposing)
        //{
    //    if (disposing == false) { return; }
    //
    //    MsmqHelper.DisposeQueue(this._messageQueue);
    //}

    //public static void DisposeQueue(MessageQueue messageQueue)
    //{
    //    if (messageQueue != null)
    //    {
    //        messageQueue.Close();
    //        messageQueue.Dispose();
    //        messageQueue = null;
    //    }
    //}
}