嵌套的mvvm-light Messenger在使用Dispatcher + multithreaded = Deadlock时发送

时间:2013-03-28 18:41:25

标签: mvvm-light

这基本上是发生了什么......

  1. A类(主线程)发送MVVM消息
    • 收到此消息,在处理过程中,构建了B类并启动后台任务。
    • 此背景发送单独的MVVM消息。
    • C类已为此消息注册,并在调度程序上调用以尝试更新UI。
    • 此时主线程仍在执行原始发送命令,线程已死锁(我可以暂停调试器,看看它们都在等待)。
  2. 其他笔记

    1. 如果我在后台线程中添加一个睡眠一秒钟(允许主线程的Send方法完成),它可以正常工作。
    2. 仅当在调度程序上调用的另一个线程上发送嵌套的MVVM消息时,才会发生这种情况。
      • 评论调度员电话......很好。
      • 不使用MVVM消息来调用调度程序......很好。
    3. 有人可以解释发生了什么吗?

1 个答案:

答案 0 :(得分:2)

我会抓住这个......

您可以在CodePlex网站上查看MVVM-Light源代码。我将在这里粘贴相关方法(为了这篇文章略微注释):

    private void SendToTargetOrType<TMessage>(TMessage message, Type messageTargetType, object token)
    {
        var messageType = typeof(TMessage);

        if (_recipientsOfSubclassesAction != null)
        {
            // Clone to protect from people registering in a "receive message" method
            // Correction Messaging BL0008.002
            var listClone =
                _recipientsOfSubclassesAction.Keys.Take(_recipientsOfSubclassesAction.Count()).ToList();

            foreach (var type in listClone)
            {
                List<WeakActionAndToken> list = null;

                if (messageType == type
                    || messageType.IsSubclassOf(type)
                    || type.IsAssignableFrom(messageType))
                {
                    lock (_recipientsOfSubclassesAction)
                    {
                        list = _recipientsOfSubclassesAction[type].Take(_recipientsOfSubclassesAction[type].Count()).ToList();
                    }
                }

                // Class A probably sends a message here from the UI thread
                SendToList(message, list, messageTargetType, token);
            }
        }

        if (_recipientsStrictAction != null)
        {
            // Class B grabs this lock on the background thread.
            // Class A continues processing on the UI thread and arrives here.
            // An attempt is made to grab the lock on the UI thread but it is
            // blocked by the background thread & Class B which in turn is waiting
            // on the UI thread. And here you have yourself a deadlock
            lock (_recipientsStrictAction)
            {
                if (_recipientsStrictAction.ContainsKey(messageType))
                {
                    var list = _recipientsStrictAction[messageType]
                        .Take(_recipientsStrictAction[messageType].Count())
                        .ToList();

                    // Class B sends its message here.
                    // Class C receives the message and does an Invoke on the UI thread
                    SendToList(message, list, messageTargetType, token);
                }
            }
        }

        RequestCleanup();
    }
  1. A类可能会在“子类收件人”选择的UI线程上发送一条消息。
  2. B类是接收此消息并启动后台任务的收件人。
  3. 然后,您的后台任务会发送一条消息,其中包含“严格操作收件人”。
  4. B类在后台线程上抓取'_recipientsStrictAction'锁。
  5. B类将消息发送到C类,C类在UI线程上进行调用。
  6. 此调用因为UI线程仍在执行第一条消息而阻塞。
  7. UI线程继续执行,然后尝试获取UI线程上的'_recipientsStrictAction'锁。不幸的是,你的后台线程(在UI线程上等待)已经有了锁。你现在陷入僵局:(
  8. 可能要考虑在C类中而不是Invoke中执行InvokeAsync。我想你可以通过这种方式避免这个问题。

    让我想知道为什么MVVM指示灯会在锁内“发送”消息。看起来像是一件不那么酷的事情。输入所有这些后,我去了CodePlex网站,看起来这个问题已被记录: http://mvvmlight.codeplex.com/workitem/7581