web-dev-qa-db-ja.com

ObservableCollectionをスレッドセーフにする方法は?

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.

UIスレッド上にないObservableCollectionを追加または削除しています。

コレクションに追加するメソッドEnqueueReportと、コレクションから削除するDequeueReportという名前があります。

手順の流れは次のとおりです。

  1. 1.新しいレポートが要求されるたびにEnqueueReportを呼び出す
  2. 数秒ごとにメソッドを呼び出して、レポートが生成されたかどうかを確認します(これには、ObservableCollectionのすべてのレポートの生成されたステータスをチェックするforeachループがあります)。
  3. レポートが生成された場合はDequeueReportを呼び出します

私はC#ライブラリにはあまり関わっていません。誰かがこれについて私を案内してくれませんか?

19
ilight

監視可能なコレクションの単純なスレッドフレンドリーバージョンを作成できます。次のように:

 public class MTObservableCollection<T> : ObservableCollection<T>
    {
        public override event NotifyCollectionChangedEventHandler CollectionChanged;
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            NotifyCollectionChangedEventHandler CollectionChanged = this.CollectionChanged;
            if (CollectionChanged != null)
                foreach (NotifyCollectionChangedEventHandler nh in CollectionChanged.GetInvocationList())
                {
                    DispatcherObject dispObj = nh.Target as DispatcherObject;
                    if (dispObj != null)
                    {
                        Dispatcher dispatcher = dispObj.Dispatcher;
                        if (dispatcher != null && !dispatcher.CheckAccess())
                        {
                            dispatcher.BeginInvoke(
                                (Action)(() => nh.Invoke(this,
                                    new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
                                DispatcherPriority.DataBind);
                            continue;
                        }
                    }
                    nh.Invoke(this, e);
                }
        }
    }

これで、大規模な検索と置換を実行し、すべてのObservableCollectionMTObservableCollectionに変更します。

7
Franck

.net framwork 4.5以降では、ネイティブコレクション同期を使用できます。

BindingOperations.EnableCollectionSynchronization(YourCollection, YourLockObject);

YourLockObjectは、任意のオブジェクトのインスタンスです。 new Object();。コレクションごとに1つ使用します。

これにより、特別なクラスなどの必要がなくなります。ただ有効にしてお楽しみください;)

[edit] MarkとEdのコメントに明記されているように(明確化に感謝します!)、これはしない更新時にコレクションをロックすると、コレクションとビューのバインディングが同期され、コレクションがスレッドセーフになりますnot[/ edit]

PS:BindingOperationsは名前空間System.Windows.Dataにあります。

26
FastJack

ここに投稿されたフランクのソリューションは、1つのスレッドが追加している場合に機能しますが、ObservableCollection自体(およびそれに基づくList)はスレッドセーフではありません。複数のスレッドがコレクションに書き込んでいる場合、追跡が困難なバグが発生する可能性があります。私は、ReaderWriteLockSlimを使用して本当にスレッドセーフになるObservableCollectionのバージョンを作成しました。

残念ながら、StackOverflowの文字制限に達したため、ここではPastebinにあります。 これは、複数のリーダー/ライターで100%機能するはずです。通常のObservableCollectionと同様に、(コールバックを受信したスレッド上の)コレクションからのコールバックでコレクションを変更することは無効です。

19
Robert Fraser

ObservableConcurrentCollectionクラスを使用できます。これらは、Parallel Extensions ExtrasライブラリでMicrosoftが提供するパッケージに含まれています。

コミュニティによってNugetで事前にビルドできます: https://www.nuget.org/packages/ParallelExtensionsExtras/

または、こちらからマイクロソフトから入手してください。

https://code.msdn.Microsoft.com/ParExtSamples

10
Greg