是什么导致我的 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();
            }
        }
    }

这是我尝试解决问题的方法,但代码已被删除:

  1. 我尝试对某些代码加锁。
  2. 我试图将某些代码放在一个单独的线程中并让它们 运行。

事情是这样的:

  1. 当我运行程序时,UI线程似乎被阻塞了。换句话说,class C 不会被绘制。有时,表格甚至不会出现。
  2. 有几次,它抱怨一个奇怪的错误"An error occurred invoking the method. The destination thread no longer exists."
  3. 如果我注释掉问题行,一切正常,但这是奇怪的部分。如果我在 class A 中创建一个定时器对象并让它以同样的方式触发事件,它工作正常。
  4. 在调试模式下单步执行时,我有时能正常工作,但大多数时候都失败了。
  5. 有几次,我 运行 进入带有消息的 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;
   }
}

类似的..