web-dev-qa-db-ja.com

ObservableCollectionとスレッド

クラスにObservableCollectionがあります。そしてクラスのさらに先にはスレッドがあります。このスレッドからObservableCollectionに追加したいと思います。しかし、私はこれを行うことができません:

このタイプのCollectionViewは、Dispatcherスレッドとは異なるスレッドからのSourceCollectionへの変更をサポートしていません。

これはUIスレッドからは発生しないため、ディスパッチャーにアクセスできません。

30
ErikTJ

JaredParのアプローチは有効です。考慮に値する別のアプローチは、組み込みObservableCollectionの代わりにスレッドセーフObservableCollectionを使用することです。そこにはいくつかの実装がありますが、 Sasha Barberの実装CLinq継続的コレクションクラス は私の意見ではより良いもののいくつかです。内部的には、これらのクラスは基本的にJaredParで概説されているアプローチを使用しますが、コレクションクラス内にカプセル化します。

16
Szymon Rozga

これを解決する最良の方法は、Dispatcherオブジェクトをバックグラウンドスレッドのstartメソッドに渡すことです。

void DoBackgroundOperation(ObservableCollection<SomeType> col) {
  var dispatcher = Dispatcher.CurrentDispatcher;
  ThreadStart start = () => BackgroundStart(dispatcher, col);
  var t = new Thread(start);
  t.Start();
}

private static void BackgroundStart(
    Dispatcher dispatcher, 
    ObservableCollection<SomeType> col) {
  ...
  SomeType t = GetSomeTypeObject();
  Action del = () => col.Add(t);
  dispatcher.Invoke(del);
}

これで、後でコレクションに追加する必要があるときに、UI Dispatcherオブジェクトを使用できます。

@Reedが指摘したように、SynchronizationContextを使用することにより、より一般的な解決策が実現されます。以下は、SynchronizationContextを使用して新しい値を追加するデリゲートを作成する関数型のサンプルです。これには、オブジェクトを作成するコードからコレクションとスレッドモデルの両方を隠すという利点があります。

void DoBackgroundOperation(ObservableCollection<SomeType> col) {
  var context = SynchronizationContext.Current;
  Action<SomeType> addFunc = (SomeType st) => context.Send(() => col.Add(st), null);
  ThreadStart start = () => BackgroundStart(addFunc);
  var t = new Thread(start);
  t.Start();
}

private static void BackgroundStart(Action<SomeType> addFunc) {
  ...
  SomeType t = GetSomeTypeObject();
  addFunc(t);
}
15
JaredPar

.Net 4.5では、スレッドセーフコレクション、ConcurrentDictionary、ConcurrentBagなど、ニーズに合った方を使用できます。 http://msdn.Microsoft.com/en-us/library/dd997305.aspx

また読んでみてください: http://www.codeproject.com/Articles/208361/Concurrent-Observable-Collection-Dictionary-and-So

6
Cornel Marian