2010-11-08 58 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