2

これが基本的に何が起こっているかです....

  1. クラス A (メイン スレッド) は MVVM メッセージを送信します
    • このメッセージが受信され、処理中にクラス B が構築され、バックグラウンド タスクが開始されます。
    • このバックグラウンドは、別の MVVM メッセージを送信します。
    • クラス C はこのメッセージを登録し、ディスパッチャーで呼び出しを行って UI の更新を試みます。
    • この時点で、メイン スレッドはまだ元の Send コマンドを実行しており、スレッドはデッドロック状態です (デバッガーを一時停止して、両方が待機していることを確認できます)。

その他の注意事項

  1. バックグラウンド スレッドに 1 秒間スリープを追加すると (メイン スレッドの Send メソッドが完了するまで)、問題なく動作します。
  2. これは、ディスパッチャーで呼び出す別のスレッドでネストされた MVVM メッセージが送信された場合にのみ発生します。
    • ディスパッチャー呼び出しをコメントアウトします...結構です。
    • MVVMメッセージを使用してディスパッチャーを呼び出すのではなく...結構です。

誰が何が起こっているのか説明できますか?

4

1 に答える 1

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 スレッドで待機している) バックグラウンド スレッドは既にロックされています。あなたは今行き詰まっています:(

Invoke ではなく、クラス C で InvokeAsync を実行することを検討してください。そうすれば、おそらく問題を回避できると思います。

MVVMライトがロックの「内側」にメッセージを送信する理由を不思議に思います。あまりクールではない種類のことのようです。これをすべて入力した後、CodePlex サイトを見回しました。これは問題が文書化されているようです: http://mvvmlight.codeplex.com/workitem/7581

于 2013-03-30T02:21:23.830 に答える