【发布时间】:2015-12-08 07:02:36
【问题描述】:
我完全迷失了真正导致问题的原因。因此,与其试图解释问题,不如直接进入有问题的代码。这是我的程序的布局:
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
}
}
上面的代码sn-p是A类的,程序启动后,这个类就连接到服务器了。连接后,此代码 sn-p 会快速触发大约 20 次,每行消息一次。 (聊天日志中已经有大约 20 行消息。)由于只有一条消息通过 if 条件,因此注释问题的行只运行一次。这些行会触发 B 类下面的代码 sn-p。
(大约在 A 类触发的时候,我有另一个像 A 的类会触发类似的事件,由 B 类以相同的方式处理,这将由 C 类处理。)
private void newSource_IncomingMessage(IChatSource sender, string user, string message)
{
UpdatedMessageEventHandler temp = UpdatedMessage;
if (temp != null)
temp(sender, user, message);
}
上面B类的代码sn-p触发下面C类的代码sn-p。
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 线程似乎被阻塞了。换句话说,C 类不会被涂漆。有时,表单甚至都不会出现。
- 有几次,它抱怨一个奇怪的错误“调用方法时发生错误。目标线程不再存在。”
- 如果我注释掉问题行,一切正常,但这是奇怪的部分。如果我在 A 类中创建一个计时器对象并让它以相同的方式触发事件,它就可以正常工作。
- 在调试模式下单步执行时,有时我可以正常工作,但大多数时候都失败了。
- 有几次,我遇到 InvalidOperationException 并显示以下消息:“控件从创建它的线程以外的线程访问”。即使我确实使它成为线程安全的。
总之,我不知道是什么导致了 UI 线程被阻塞。任何指针或我可能做错了什么?
【问题讨论】:
-
看来
connection_OnMessage是从另一个线程调用的。 你能显示连接的初始化吗?connection.connect是否阻塞了 gui 线程?所以调用会导致挂起。 -
当您使用调试器和 UI 线程块运行此程序时,您可以暂停进程并查看当时正在运行的线程以及它们在做什么。你能说明会发生什么吗? UI 线程应该等待某个调用返回,知道是哪个调用可能会有所帮助。
-
在
updateMessage中检查allDataGridView.InvokeRequired,但在this上调用Invoke而不是allDataGridView。这很可能无关紧要,只是为了确保您的所有控件都在一个线程上。顺便说一句:也许BeginInvoke而不是Invoke也有帮助,但首先我们应该找出它为什么会挂起。 -
"4. .... 但大多数情况下,它会失败" 究竟是什么意思?
-
@JeroenvanLangen,connection_OnMessage 是一个事件处理程序,当有来自连接的消息时触发。如下初始化:
connection.Open(); connection.OnLogin += new ObjectHandler(connection_OnLogin); connection.OnError += new ErrorHandler(connection_OnError); connection.OnMessage += new agsXMPP.protocol.client.MessageHandler(connection_OnMessage);
标签: c# multithreading winforms event-handling thread-safety