mvvm-light - 嵌套 mvvm-light Messenger 在使用 Dispatcher 时发送 + 多线程 = 死锁

标签 mvvm-light

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

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

其他说明

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

谁能解释一下这是怎么回事?

最佳答案

我会尝试一下......

您可以在其 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 light 会在锁“内部”发送消息。似乎是一件不太酷的事情。输入所有这些内容后,我浏览了 CodePlex 网站,看起来这个问题已被记录: http://mvvmlight.codeplex.com/workitem/7581

关于mvvm-light - 嵌套 mvvm-light Messenger 在使用 Dispatcher 时发送 + 多线程 = 死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15689747/

相关文章:

c# - 如何通过 EventToCommand 将 LayoutRoot 发送到 RelayCommand?

c# - 将 IsEnabled 绑定(bind)到父 ViewModel 而不是 UserControl ViewModel

xaml - 使用 MVVM 在 XAML 中让 ViewModel 知道用户何时滚动到 ListView 的末尾

wpf - MVVM Light - 继电器命令参数值间歇性出现

wpf - 如何使用带有可编辑组合框的 EventToCommand 将 TextBoxBase.TextChanged 与命令绑定(bind)?

wpf - 正确的 MVVM 设计模式 - ViewModel 和 DataContext

c# - MVVM Light 中有两种 ViewModel 吗?

xaml - MVVM Light ViewModelLocator

c# - MVVM Light SimpleIoC 与 Ninject

xamarin - 如何在 mvvmlight 中实现自定义导航服务