是什么导致我的 UI 线程被阻止?
What is causing my UI Thread to be blocked?
我完全迷失了真正导致问题的原因。因此,与其试图解释问题,我还不如直接进入有问题的代码。这是我的程序的布局:
private void connection_OnMessage(object sender, agsXMPP.protocol.client.Message msg)
{
if (!string.IsNullOrEmpty(msg.Body) && ((msg.XDelay != null && msg.XDelay.Stamp.Date == DateTime.Today) || msg.XDelay == null))
{
agsXMPP.Jid JID = new Jid(msg.From.Bare);
int rowIndex = chatLog.Rows.Add();
chatLog.Rows[rowIndex].Cells["chatNameColumn"].Value = JID.User;
chatLog.Rows[rowIndex].Cells["chatMessageColumn"].Value = msg.Body;
//Begin line of the problem
if (IncomingMessage != null)
IncomingMessage(this, JID.User, msg.Body);
//End of the problem
}
}
上面的代码片段是class A。启动程序后,这个class连接到服务器。连接后,此代码片段会快速触发约 20 次,每行消息一次。 (聊天记录中已经有大约20行消息。)由于只有一条消息通过了if条件,所以评论问题的行只有运行一次。这些行触发了 class B.
下面的代码片段
(在 class A 触发期间,我有另一个像 A 一样的 class 触发类似的事件,由 class B 以相同的方式处理,这将是由 class C.)
处理
private void newSource_IncomingMessage(IChatSource sender, string user, string message)
{
UpdatedMessageEventHandler temp = UpdatedMessage;
if (temp != null)
temp(sender, user, message);
}
上面 class B 的代码片段触发下面 class C 的代码片段。
private void chatManager_UpdatedMessage(IChatSource source, string user, string message)
{
if (!source.Muted)
{
updateMessage(source, user, message);
}
}
delegate void UpdateMessageCallback(IChatSource source, string user, string message);
private void updateMessage(IChatSource source, string user, string message)
{
if (allDataGridView.InvokeRequired)
{
UpdateMessageCallback d = new UpdateMessageCallback(updateMessage);
Invoke(d, new object[] { source, user, message });
}
else
{
int row = allDataGridView.Rows.Add();
allDataGridView.Rows[row].DefaultCellStyle.ForeColor = source.TextColor;
allDataGridView.Rows[row].Cells["NameColumn"].Value = user;
allDataGridView.Rows[row].Cells["MessageColumn"].Value = message;
if (!MenuItem.Checked)
{
MenuItem.Checked = true;
Show();
}
}
}
这是我尝试解决问题的方法,但代码已被删除:
- 我尝试对某些代码加锁。
- 我试图将某些代码放在一个单独的线程中并让它们 运行。
事情是这样的:
- 当我运行程序时,UI线程似乎被阻塞了。换句话说,class C 不会被绘制。有时,表格甚至不会出现。
- 有几次,它抱怨一个奇怪的错误"An error occurred invoking the method. The destination thread no longer exists."
- 如果我注释掉问题行,一切正常,但这是奇怪的部分。如果我在 class A 中创建一个定时器对象并让它以同样的方式触发事件,它工作正常。
- 在调试模式下单步执行时,我有时能正常工作,但大多数时候都失败了。
- 有几次,我 运行 进入带有消息的 InvalidOperationException,"Control accessed from a thread other than the thread it was created on." 即使我确实使它成为线程安全的。
总之,不知道是什么原因导致UI线程被阻塞。任何指示或我可能做错了什么?
问题是您正在跨线程调用方法。这可能会导致死锁。
您可以解决这个问题,将消息添加到并发队列和计时器(GUI 线程)上检查队列并将消息添加到控件。
这不是一个完整的解决方案,而是一种防止跨线程方法调用的方法
赞:(PSEUDO)(在网站上在线撰写)
// dataholder
public class ChatMsg
{
public string User {get;set;}
public string Message {get;set;}
}
// message store
private List<ChatMsg> _messages = new List<ChatMsg>();
// timer
private Timer _timer;
// callback for you chatapi?? (like you wrote)
private void newSource_IncomingMessage(IChatSource sender, string user, string message)
{
UpdatedMessageEventHandler temp = UpdatedMessage;
// lock the store
lock(_messages)
_messages.Add(new ChatMsg { User = user, Message = message });
}
// constructor
public Form1()
{
// create the check timer.
_timer = new Timer();
_timer.Interval = 100;
_timer.Tick += Timer_Tick;
_timer.Start();
}
// timer method
private void Timer_Tick(object sender, EventArgs e)
{
// copy of the queue
ChatMsg[] msgs;
// lock the store and 'move' the messages
lock(_messages)
{
msgs = _messages.ToArray();
_messages.Clear();
}
if(msgs.Length == 0)
return;
// add them to the controls
foreach(var msg in msgs)
{
// add the message to the gui controls... (copied from your question)
int row = allDataGridView.Rows.Add();
allDataGridView.Rows[row].DefaultCellStyle.ForeColor = source.TextColor;
allDataGridView.Rows[row].Cells["NameColumn"].Value = user;
allDataGridView.Rows[row].Cells["MessageColumn"].Value = message;
}
}
类似的..
我完全迷失了真正导致问题的原因。因此,与其试图解释问题,我还不如直接进入有问题的代码。这是我的程序的布局:
private void connection_OnMessage(object sender, agsXMPP.protocol.client.Message msg)
{
if (!string.IsNullOrEmpty(msg.Body) && ((msg.XDelay != null && msg.XDelay.Stamp.Date == DateTime.Today) || msg.XDelay == null))
{
agsXMPP.Jid JID = new Jid(msg.From.Bare);
int rowIndex = chatLog.Rows.Add();
chatLog.Rows[rowIndex].Cells["chatNameColumn"].Value = JID.User;
chatLog.Rows[rowIndex].Cells["chatMessageColumn"].Value = msg.Body;
//Begin line of the problem
if (IncomingMessage != null)
IncomingMessage(this, JID.User, msg.Body);
//End of the problem
}
}
上面的代码片段是class A。启动程序后,这个class连接到服务器。连接后,此代码片段会快速触发约 20 次,每行消息一次。 (聊天记录中已经有大约20行消息。)由于只有一条消息通过了if条件,所以评论问题的行只有运行一次。这些行触发了 class B.
下面的代码片段(在 class A 触发期间,我有另一个像 A 一样的 class 触发类似的事件,由 class B 以相同的方式处理,这将是由 class C.)
处理 private void newSource_IncomingMessage(IChatSource sender, string user, string message)
{
UpdatedMessageEventHandler temp = UpdatedMessage;
if (temp != null)
temp(sender, user, message);
}
上面 class B 的代码片段触发下面 class C 的代码片段。
private void chatManager_UpdatedMessage(IChatSource source, string user, string message)
{
if (!source.Muted)
{
updateMessage(source, user, message);
}
}
delegate void UpdateMessageCallback(IChatSource source, string user, string message);
private void updateMessage(IChatSource source, string user, string message)
{
if (allDataGridView.InvokeRequired)
{
UpdateMessageCallback d = new UpdateMessageCallback(updateMessage);
Invoke(d, new object[] { source, user, message });
}
else
{
int row = allDataGridView.Rows.Add();
allDataGridView.Rows[row].DefaultCellStyle.ForeColor = source.TextColor;
allDataGridView.Rows[row].Cells["NameColumn"].Value = user;
allDataGridView.Rows[row].Cells["MessageColumn"].Value = message;
if (!MenuItem.Checked)
{
MenuItem.Checked = true;
Show();
}
}
}
这是我尝试解决问题的方法,但代码已被删除:
- 我尝试对某些代码加锁。
- 我试图将某些代码放在一个单独的线程中并让它们 运行。
事情是这样的:
- 当我运行程序时,UI线程似乎被阻塞了。换句话说,class C 不会被绘制。有时,表格甚至不会出现。
- 有几次,它抱怨一个奇怪的错误"An error occurred invoking the method. The destination thread no longer exists."
- 如果我注释掉问题行,一切正常,但这是奇怪的部分。如果我在 class A 中创建一个定时器对象并让它以同样的方式触发事件,它工作正常。
- 在调试模式下单步执行时,我有时能正常工作,但大多数时候都失败了。
- 有几次,我 运行 进入带有消息的 InvalidOperationException,"Control accessed from a thread other than the thread it was created on." 即使我确实使它成为线程安全的。
总之,不知道是什么原因导致UI线程被阻塞。任何指示或我可能做错了什么?
问题是您正在跨线程调用方法。这可能会导致死锁。
您可以解决这个问题,将消息添加到并发队列和计时器(GUI 线程)上检查队列并将消息添加到控件。
这不是一个完整的解决方案,而是一种防止跨线程方法调用的方法
赞:(PSEUDO)(在网站上在线撰写)
// dataholder
public class ChatMsg
{
public string User {get;set;}
public string Message {get;set;}
}
// message store
private List<ChatMsg> _messages = new List<ChatMsg>();
// timer
private Timer _timer;
// callback for you chatapi?? (like you wrote)
private void newSource_IncomingMessage(IChatSource sender, string user, string message)
{
UpdatedMessageEventHandler temp = UpdatedMessage;
// lock the store
lock(_messages)
_messages.Add(new ChatMsg { User = user, Message = message });
}
// constructor
public Form1()
{
// create the check timer.
_timer = new Timer();
_timer.Interval = 100;
_timer.Tick += Timer_Tick;
_timer.Start();
}
// timer method
private void Timer_Tick(object sender, EventArgs e)
{
// copy of the queue
ChatMsg[] msgs;
// lock the store and 'move' the messages
lock(_messages)
{
msgs = _messages.ToArray();
_messages.Clear();
}
if(msgs.Length == 0)
return;
// add them to the controls
foreach(var msg in msgs)
{
// add the message to the gui controls... (copied from your question)
int row = allDataGridView.Rows.Add();
allDataGridView.Rows[row].DefaultCellStyle.ForeColor = source.TextColor;
allDataGridView.Rows[row].Cells["NameColumn"].Value = user;
allDataGridView.Rows[row].Cells["MessageColumn"].Value = message;
}
}
类似的..