3

MVVMでモデルをスレッドセーフにするために何をする必要があるのか​​疑問に思っています。シングルトンとしてインスタンス化される次のクラスがあるとします。

public class RunningTotal: INotifyPropertyChange
{
   private int _total;
   public int Total
   {
      get { return _total; }
      set
      {
         _total = value;
         PropertyChanged("Total");
      }
   }
   ...etc...
}

私のビューモデルは、プロパティを介してそれを公開します:

public RunningTotal RunningTotal { get; }

そして、私のビューにはテキストブロックがバインドされてい{Binding Path=RunningTotal.Total}ます。

私のアプリには、Totalの値を定期的に更新するバックグラウンドスレッドがあります。Totalを更新するものが他にないと仮定すると、このすべてをスレッドセーフにするために(もしあれば)何をすべきですか?

さて、私が似たようなことをしたいが、タイプDictionary<>、またはのプロパティを使用したい場合はどうなりObservableCollection<>ますか?どのメンバー(追加、削除、クリア、インデクサー)がスレッドセーフですか?代わりにConcurrentDictionaryを使用する必要がありますか?

4

6 に答える 6

8

私のアプリには、Totalの値を定期的に更新するバックグラウンドスレッドがあります。Totalを更新するものが他にないと仮定すると、このすべてをスレッドセーフにするために(もしあれば)何をすべきですか?

スカラープロパティの場合、特別なことをする必要はありません。PropertyChangedイベントは自動的にUIスレッドにマーシャリングされます。

さて、Dictionary<>型またはObservableCollection<>型のプロパティを使用して、同様のことをしたい場合はどうなりますか?どのメンバー(追加、削除、クリア、インデクサー)がスレッドセーフですか?代わりにConcurrentDictionaryを使用する必要がありますか?

いいえ、これはスレッドセーフではありません。バックグラウンドスレッドからのコンテンツを変更するObservableCollection<T>と、壊れます。UIスレッドでそれを行う必要があります。これを行う簡単な方法は、ここで説明するように、UIスレッドでイベントを発生させるコレクションを使用することです。

についてDictionary<TKey, TValue>は、コンテンツが変更されても通知が出されないため、UIはとにかく通知されません。

于 2012-06-13T13:22:17.633 に答える
2

モデルは、他のコードと同じようにスレッドセーフな方法で作成する必要があります。ロック、並行コンテナ、またはその他の方法を使用しているかどうかを判断するのはあなた次第です。モデルは単なるライブラリコードであり、その機能がMVVMアプリケーションによって消費されることを(ほとんど)知らないはずです。

ただし、VMはUIスレッドで動作する必要があります。つまり、通常、モデルからのイベントがUIスレッドで発生することを信頼できないため、サブスクライブされたイベントがUIスレッドで発生しない場合は、呼び出しをマーシャリングするか、タスクキューに保存する必要があります。

したがって、VMは、モデルが必要とする以上に、特定の方法でスレッドセーフを考慮する必要がある場所です。

次に、ビューコードは通常、すべてのスレッドの問題について無知で生きることができます。専用のUIスレッドですべてのメッセージ/呼び出し/イベントなどを取得し、UIスレッドでも独自の呼び出しを行います。


特にあなたの場合、あなたのコードはモデルではなくVMですよね?この場合、UIスレッドでイベントを発生させる必要があります。そうしないと、ビューが不幸になります。

于 2012-06-13T13:18:07.363 に答える
2

更新するスレッドが2つありTotal、すべての変更をメソッドに記録するとし_totalますPropertyChanged。現在PropertyChanged、値を見逃す可能性のある競合状態があります。これは、スレッドが呼び出しの途中でブロックしたときに発生しますset_Total。更新されます_totalが、PropertyChangedはまだ呼び出されます。その間に、別のスレッド_totalが別の値に更新されます。

thread1: _total = 4;
thread2: _total = 5;
thread2: PropertyChanged("Total");
thread1: PropertyChanged("Total");

現在、PropertyChanged4の値で呼び出されることはありません。

これを解決するには、値をPropertyChangedメソッドに渡すか、セッターのロックを使用します。

このプロパティを更新するスレッドが1つあると言うので、競合状態になる可能性はありません。これは、複数のスレッド(またはプロセス)が同時に同じものを更新する場合にのみ当てはまります。

于 2012-06-13T13:20:15.747 に答える
1

この質問は、ObservableCollectionのスレッドマーシャリングバージョンを提供します

バックグラウンドスレッドからデータバインドされたdatagridviewを正しく更新するにはどうすればよいですか

ただし、スレッド間の競合について心配する必要があります。これにより、リソースが更新されたときにリソースをロックするか、Interlocked.Incrementなどを使用する必要があります。

1つのスレッドが更新中で、別のスレッドが読み取り中の場合、更新の途中で読み取りが行われる可能性があります(たとえば、Int64が変更されています。前半(32ビット)が1つのスレッドで更新され、後半が更新される前に、値が2番目のスレッドから読み取られます。完全に間違った値が読み取られます)

これは、アプリケーションが結果として何を実行するかによって、問題になる場合と問題にならない場合があります。間違った値がGUIで1秒間フラッシュされる場合、それはおそらく大したことではなく、ロックのパフォーマンスの低下は無視できます。プログラムがその値に基づいてアクションを実行する場合は、おそらくそれをロックダウンする必要があります。

于 2012-06-13T13:28:27.770 に答える
0

簡単な答えは、UIスレッドのディスパッチャーを介してUIスレッドでプロパティの更新をスケジュールする必要があるということです。これにより、更新操作がキューに入れられ、アプリケーションがクラッシュしなくなります。

private void handler(object sender, EventArgs e)
{
    Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate { updates(); });
}

private void updates() { /* real updates go here */ }
于 2015-01-22T02:37:21.023 に答える
0

ビューでビューモデルをインスタンス化するときは、ディスパッチャをctorに渡すだけです。

    ServerOperationViewmodel ViewModel;        
    public pgeServerOperations()
    {
        InitializeComponent();
        ViewModel = new ServerOperationViewmodel(Dispatcher);
    }

次に、ビューモデルで:

Dispatcher UIDispatcher;
public ServerOperationViewmodel(Dispatcher uiDisp)
{
    UIDispatcher = uiDisp;
}

そして、通常のUIディスパッチャーのように使用します。

UIDispatcher.Invoke(() =>
{
  .......
});

私はまだMVVMにかなり慣れていないことを認めますが、これがMVVMのモットーを破るとは思いません。

于 2015-03-18T22:30:57.283 に答える