我在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!");
}
}
是的,這可能不是最佳的,但它也不是問題的原因,因爲消息框在complete設置爲true後顯示(所以while循環會中斷)。 – 2010-11-08 07:49:56
我改變了代碼,使用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
@Hach - 與內存屏障無關,這是應該使用* volatile *關鍵字的地方。但是,不要,ARE是優越的。 – 2010-11-08 10:33:22