2010-11-08 57 views
0

我在C#中的處理線程遇到問題。基本上,線程在新消息到達或發送時管理聊天窗口,不幸的是,我根據運行環境出現了不同的情況。Application.Run()在不包含調試器的版本構建中運行方式不同於調試版本

當運行Debug版本(無論是否帶有調試器)或Release版本在調試器下時,Process()函數正確運行,顯示窗口並收到正確的消息。

然而,當運行一個沒有調試器的Release版本時,Application.Run()調用似乎停止處理主進程()線程(注意這個調用發生在處理線程的一個子線程下)因此不再發生處理。

通過使用MessageBox.Show()調用我已經確定Application.Run()是最後一次調用之前沒有更多的消息框顯示(他們應該是因爲它顯示了多少消息是每次while循環運行時收到)。

有沒有人知道爲什麼Application.Run()調用在這種情況下表現不同?

/// <summary> 
    /// Processes the IM message queue, managing the chat windows and messages. 
    /// </summary> 
    private void Process() 
    { 
     try 
     { 
      MessageBox.Show("MessageQueue process has started!"); 

      while (this.m_Running) 
      { 
       List<Message> messages = null; 
       lock (this.m_Lock) 
       { 
        messages = new List<Message>(this.m_Messages); 
        MessageBox.Show("MessageQueue received " + this.m_Messages.Count + " messages on this spin."); 
        this.m_Messages.Clear(); 
       } 

       // Process all the messages 
       foreach (Message m in messages) 
       { 
        Contact c = m.Contact; 

        if (!this.m_Windows.Keys.Contains(c.ID) || this.m_Windows[c.ID] == null) 
        { 
         MessageBox.Show("MessageQueue is creating a new window."); 
         bool complete = false; 
         Thread t = new Thread(() => 
          { 
           try 
           { 
            ChatWindow w = new ChatWindow(this, c, new Contact(this.m_Client.JID, null)); 
            w.Load += (sender, e) => 
             { 
              if (m.IsTo) 
               w.AppendSentMessage(m.To, m.Data); 
              else if (m.IsFrom) 
               w.AppendRecievedMessage(m.From, m.Data); 

              w.UpdateStatus(c); 
             }; 
            w.FormClosed += (sender, e) => 
             { 
              this.m_Windows[c.ID] = null; 
             }; 
            c.StatusUpdated += (sender, e) => 
             { 
              RoketPack.Manager.VoidLambda lambda =() => 
              { 
               w.UpdateStatus(c); 
              }; 

              if (w.InvokeRequired) 
               w.Invoke(lambda); 
              else 
               lambda(); 
             }; 
            MessageBox.Show("MessageQueue is now showing the new window."); 
            w.Show(); 
            if (!this.m_Windows.Keys.Contains(c.ID)) 
             this.m_Windows.Add(c.ID, w); 
            else 
             this.m_Windows[c.ID] = w; 
            complete = true; 
            MessageBox.Show("MessageQueue is now running the new window."); 
            Application.Run(w); 
            MessageBox.Show("MessageQueue is now closing the window."); 
           } 
           catch (Exception ex) 
           { 
            MessageBox.Show(ex.ToString()); 
            complete = true; 
           } 
          }); 
         t.Name = "IM Chat Window - " + c.ID; 
         t.IsBackground = true; 
         t.Start(); 

         // We have to wait until the form has been added to the dictionary. 
         while (!complete) ; 
        } 
        else 
        { 
         RoketPack.Manager.VoidLambda lambda =() => 
          { 
           if (m.IsTo) 
            this.m_Windows[c.ID].AppendSentMessage(m.To, m.Data); 
           else if (m.IsFrom) 
            this.m_Windows[c.ID].AppendRecievedMessage(m.From, m.Data); 
           MessageBox.Show("MessageQueue appended the message to the chat window."); 
          }; 

         MessageBox.Show("MessageQueue received a message and is now forwarding it onto the chat window."); 
         if (this.m_Windows[c.ID].InvokeRequired) 
          this.m_Windows[c.ID].Invoke(lambda); 
         else 
          lambda(); 
        } 
       } 

       // Sleep for 10 milliseconds. 
       Thread.Sleep(10); 
      } 
     } 
     finally 
     { 
      MessageBox.Show("MessageQueue process has terminated!"); 
     } 
    } 

回答

1

除了什麼leppie寫道,這看起來像一個壞的起點:

while (!complete); 

我不知道到底是什麼,保證有大約懸掛變量和知名度,但緊循環幾乎總是一個壞理念。

使用Wait/Notify方法或Auto/ManualResetEvent通常會更好。

+0

是的,這可能不是最佳的,但它也不是問題的原因,因爲消息框在complete設置爲true後顯示(所以while循環會中斷)。 – 2010-11-08 07:49:56

+0

我改變了代碼,使用Wait/Notify方法和w.ShowDialog()來代替,它似乎解決了這個問題。我相信這是CLR優化的問題,如http://www.albahari.com/threading/part4.aspx#_Memory_Barriers_and_Volatility中所述,可能會導致諸如完整布爾值在添加字典值之前設置爲true的問題(這當然會導致線程崩潰)。添加Wait/Notify方法隱式地通過所需的鎖定語句給出了內存屏障。 – 2010-11-08 10:21:53

+0

@Hach - 與內存屏障無關,這是應該使用* volatile *關鍵字的地方。但是,不要,ARE是優越的。 – 2010-11-08 10:33:22

1

你有沒有意識到Application.Run不會返回,直到你的應用程序關閉?

它似乎也是你從一個子線程調用Application.Run

+0

Application.Run的描述是「開始在*當前線程*上運行標準應用程序消息循環,並使指定表單可見。」這意味着獨立於任何其他線程,這正是在調試器下運行Debug版本或運行Release版本時發生的情況。唯一的例外是當你在沒有調試器的Release版本下運行它時,這就是它在應用程序範圍內的情況。 – 2010-11-08 07:48:06

+0

我應該補充說,沒有Application.Run()調用,創建的表單會簡單地鎖定,因爲線程終止後沒有爲它處理消息循環。 – 2010-11-08 08:28:34