【问题标题】:Nested mvvm-light Messenger sends while using the Dispatcher + multithreaded = Deadlock嵌套 mvvm-light Messenger 在使用 Dispatcher 时发送 + 多线程 = 死锁
【发布时间】:2013-03-28 18:41:25
【问题描述】:

这基本上是正在发生的事情......

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

其他说明

  1. 如果我在后台线程中添加睡眠一秒钟(允许主线程的 Send 方法完成),它工作正常。
  2. 只有在调用调度程序的另一个线程上发送嵌套的 MVVM 消息时才会发生这种情况。
    • 注释掉调度程序调用...很好。
    • 不使用 MVVM 消息来调用调度程序...很好。

谁能解释这是怎么回事?

【问题讨论】:

    标签: mvvm-light


    【解决方案1】:

    我会尝试一下...

    您可以在其 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 线程)已经获得了锁。你现在陷入僵局:(

    可能要考虑在 C 类中执行 InvokeAsync 而不是 Invoke。我认为您可能可以通过这种方式避免这个问题。

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

    【讨论】:

      猜你喜欢
      • 2013-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-16
      • 1970-01-01
      相关资源
      最近更新 更多