何度も何度も、BindingList と ObservableCollection のスレッド セーフ バージョンを作成する必要があることに気付きました。UI にバインドすると、これらのコントロールを複数のスレッドから変更できないためです。私が理解しようとしているのは、なぜこれが事実なのかということです-それは設計上の欠陥ですか、それともこの動作は意図的なものですか?
4 に答える
問題は、スレッド セーフ コレクションの設計が単純ではないことです。確かに、状態を壊すことなく複数のスレッドから変更/読み取りできるコレクションを設計するのは簡単です。しかし、コレクションが複数のスレッドから更新されることを考えると、使用可能なコレクションを設計することははるかに困難です。次のコードを例に取ります。
if ( myCollection.Count > 0 ) {
var x = myCollection[0];
}
myCollection はスレッド セーフなコレクションであり、追加と更新によって状態が破損しないことが保証されているとします。このコードはスレッド セーフではなく、競合状態です。
なんで?myCollection は安全ですが、myCollection への 2 つのメソッド呼び出し (Count とインデクサー) の間で変更が発生しないという保証はありません。別のスレッドが入ってきて、これらの呼び出しの間のすべての要素を削除できます。
このタイプの問題は、率直に言って、このタイプのコレクションを使用することを悪夢にします。1 つの呼び出しの戻り値がコレクションの後続の呼び出しに影響を与えることはできません。
編集
最近のブログ投稿でこの議論を拡張しました: http://blogs.msdn.com/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx
Jared の優れた回答に少し付け加えると、スレッド セーフは無料ではありません。多くの (ほとんどの?) コレクションは、単一のスレッド内でのみ使用されます。マルチスレッドのケースに対処するために、これらのコレクションにパフォーマンスや機能のペナルティが必要なのはなぜですか?
他のすべての回答からアイデアを収集すると、これが問題を解決する最も簡単な方法だと思います。
質問を次のように変更します。
「なぜクラスXは正気ではないのですか?」
に
「クラス X でこれを行うには、どのような方法が適切ですか?」
クラスのコンストラクターで、監視可能なコレクションを作成するときに現在のディスプラチャーを取得します。ご指摘のとおり、元のスレッドで変更を行う必要があるため、メインのGUI スレッドではない可能性があります。したがって、 App.Current.Dispatcherは常に正しいわけではなく、すべてのクラスにthis.Dispatcherがあるわけではありません。
_dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher; _data = new ObservableCollection<MyDataItemClass>();
ディスパッチャを使用して、元のスレッドが必要なコード セクションを呼び出します。
_dispatcher.Invoke(new Action(() => { _data.Add(dataItem); }));
それはあなたのためにトリックをするはずです。.Invokeの代わりに.BeginInvokeを好む状況もありますが。
夢中になりたい場合は、UI スレッドで自動的に通知を返す があります。ThreadedBindingList<T>
ただし、一度に 1 つのスレッドだけが更新などを行うのは安全です。