2012-03-20 34 views
5

我在其他帖子上看到過這個錯誤,但不是針對這種確切情況。MessageQueue多於一次丟棄

我有兩個類與MessageQueue做同樣的事情。因此,我將隊列的創建和處理抽象爲助手類。我收到這個錯誤,我看不到隊列可以多次處理。

對象「消息隊列」可以設置比在方法「MsmqHelper.DisposeQueue(的MessageQueue)」

在類中的一個,一旦這是隊列如何使用更多:

private MessageQueue _messageQueue; 

然後在類的構造函數:

this._messageQueue = MsmqHelper.InitializeQueue(); 

不在於它真的很重要,但對完整,這裏是其中使用的隊列:

this._messageQueue.Send(workflowCreated); 

而且這裏的處置方法:

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

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

    MsmqHelper.DisposeQueue(this._messageQueue); 
} 

而且這是在助手類,它實際上是調用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()而不是兩者。

+0

其中定義了'public void Dispose()(bool disposing)'方法嗎? – Tigran 2012-03-20 21:14:35

+0

這可能是因爲'Close'和'Dispose'做同樣的事情嗎? – 2012-03-20 21:24:23

+0

@Lasse這是一個有趣的想法。我評論了Close()行並編譯了它。然後我取消註釋了Close()行,並對Dispose()行進行了註釋,並且它仍然被編譯。所以你所說的可能是答案。我可以發誓我在前面的代碼中都有兩行(Close()和Dispose()),並且它工作正常。我剛剛檢查過,而我做過。現在我不知道該怎麼想。 – 2012-03-20 21:29:56

回答

2

關閉將釋放的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的開銷。

* UPDATE * 似乎CA對待CA2202爲不同成員字段與傳遞一次性對象的方法,即使該方法是私有的類。無論如何,根據文檔,你只需要調用Close()或Dispose(),但不能同時調用兩者。然而,我建議改變你的設計,以便在你的消息操作範圍內創建,使用和關閉MessageQueue對象,就像上面演示的文檔示例中的示例一樣。

+1

我也這麼認爲,但事實並非如此。看到我上面的編輯3。 – 2012-03-21 03:14:02

+0

您可以成功調用兩者,但爲什麼CA在一個案例中抱怨,而其他案件不在我身邊。話雖如此,你可能應該打開MessageQueue,使用它,並儘快關閉或處置它。僅當場景需要在垃圾收集之前重新獲取資源時才使用close。否則,連接緩存允許後續的MessageQueue對象被重新創建/打開,而且開銷很小。 – Jim 2012-03-21 03:28:31

+1

謝謝,吉姆。我接受你的回答,因爲這最能說明情況:「看起來CA對待成員字段的CA2202與將可丟棄對象傳遞給方法的方式不同,即使該方法對於類是私有的。」 – 2012-03-21 16:10:51

2

是的。這可以多次處理對象:

this._messageQueue評估的值爲而不是在調用MsmqHelper.DisposeQueue(this._messageQueue)後更改。

只有本地參數(名爲messageQueue)在DisposeQueue方法被分配的值null。因此,「無效後衛」無法正確保衛隨後的時間。 (這是因爲C#的默認行爲是Call-By-Value:請參見鏈接來了解這意味着什麼‘傳遞給一個對象的引用的值’的情況下)。

要麼採取ref或來電分配this._messageQueue = null

+0

啊,所以我沒有設置隊列爲空,我將變量(指向對象的指針)設置爲空。我只是在DisposeQueue()方法中使用參數。這意味着this._messageQueue仍然指向引用,並且從不設置爲null。是對的嗎? – 2012-03-20 21:23:32

+0

One * never *將對象設置爲null - 只有變量;-)'this._messageQueue'仍然*評估爲*與之前所做的相同的對象,是的。 (在討論類類型時,「評估爲」可以理解爲「存儲參考」) – 2012-03-20 21:25:02

+0

請注意,編譯器抱怨DisposeQueue方法,並且此方法只會在處理實際的對象時多次拋棄對象*多次呼叫*,但抱怨是關於方法本身,而不是多次呼叫它。我認爲儘管做了同樣的事情(即處理對象),但更多的是警告是關於'Close'和'Dispose'的調用。 – 2012-03-20 21:29:16

1

如果的MessageQueue類實現IDisposable iterface,那麼就沒有指向明確地使用Dispose方法和Close()方法,因爲在所有這些類Close()方法通常不是一個iterface方法,而是一個類的方法。通常,在Dispose方法中,在釋放託管/非託管資源之前,所有正確的應用程序都應該調用Close()方法。

再次,通過推動外部靜態幫助,你打破了一次性模式。這不是控制物體壽命的正確方法;你並不需要亂用一次性的圖案,你可以簡單地使用它

而且你的代碼可能會被簡化,這樣的:

// 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; 
    // } 
    //} 
} 
+0

Artur和Jim:謝謝你的回答。我會改變我的實施來使用你的建議。儘管你的回答並不能解釋爲什麼我會在一種情況下得到CA錯誤,但是不能解決另一種情況,你的答案仍然有幫助。問題是,完全相同的代碼在一種方法中編譯,但不是另一種。 – 2012-03-21 14:31:09