ページ

2011年6月16日木曜日

[.NET] スレッドセーフな ObservableCollection<T> が欲しい

Windows Phone 7 のコードを書いていると今まで以上に UI スレッドをブロックしたくなくなるんですよね。(ぬるぬるにしたいからw)

で、今更ながらに ObservableCollection<T> についてどうにかならないか考えてたんですが、どうにもなりそうにないですねぇ。
ObservableCollection<T> は INotifyCollectionChanged を実装してコレクションに変更があったことを通知してくれるようになってるわけですが、この INotifyCollectionChanged の CollectionChanged イベントは 「何番目の要素が変更されたか」 といった情報も伝えるようになってます (NotifyCollectionChangedEventArgs 参照)。で、CollectionChanged イベントを受け取るのは ListBox などの UI 要素であることが多いわけです。必ずしもそうと決まっているわけではないですが、ObservableCollection<T> を使うのは ListBox などの ItemsSource にしたいから、という場合がほとんどじゃないかと思います。そして、ListBox などの UI 要素のほとんどの機能は UI スレッド以外から呼び出すことはできません。呼び出すと例外が発生します。そのため、CollectionChanged イベントも UI スレッド以外で発生させるのは危険ということになります。
ObservableCollection<T> に対して Insert や Remove などを行うと自動的に CollectionChanged イベントが発生するわけですから、結局のところ UI スレッド以外でこういったコレクションの内容を変更することはできないわけです。もちろん、Insert や Remove の処理自体は別スレッドで行って、CollectionChanged イベントの発行のみ UI スレッドでやるという実装も可能ですが、CollectionChanged イベントの呼び出しが終わるまでコレクションの内容が NotifyCollectionChangedEventArgs の内容と整合性が取れていることを保証しなくちゃいけませんから、結局のところあんまり別スレッドで Insert、Remove などの処理を行う意味は無いって感じになってしまいます。

ObservableCollection<T> 単体で考えればスレッドセーフにする意味があるかもしれませんが、ListBox の ItemsSource にデータバインディングしている ObservableCollection<T> と考えると ListBox の中身まで含めて都合をつけなくちゃいけないのでどうにも手の出しようがないって感じですね。
こういう風に考えると、この記事のタイトルは 「スレッドセーフな ObservableCollection<T> が欲しい」 ではなくて、「UI 要素に任意のスレッドからアクセスできるようにして欲しい」 というところに行き着いちゃうわけです。

「ObservableCollection threadsafe」 で検索するとすぐに Thread safe observable collection この記事が見つかりました。
見てみると、これもやはりほとんどのメソッドを Dispatcher 経由で UI スレッドにスイッチしてますね。参照系は ReaderWriterLock で別スレッドからでもアクセス可にしてあるので、その部分はパフォーマンス向上に繋がるかもしれませんが、それくらいかなぁ?
あと、この例では Dispatcher.Invoke() メソッドを使って UI スレッドに同期的にスイッチしていますが、Silverlight/WP7 には Invoke() は無いんですよねぇ。(BeginInvoke() しかありません)
Dispatcher.BeginInvoke() を使って非同期的に UI スレッドにスイッチするとなると、上にも書いたように UI スレッド側での CollectionChanged イベントの呼び出しが終わるまでコレクションの内容を維持してやるとかいった仕組みが必要になります。(CollectionChanged イベントの呼び出しが終わるまで次の Insert や Remove の実行をブロックするとか) そういう実装もやれないことは無いですが、あんまりパフォーマンスは良くならないような感じがしちゃいます。

と、言うわけで、この件は考えるのやめましたw
何かいいアイデアがあったら教えてください。

1 件のコメント:

  1. 倉田 智朗2011年7月12日 7:57

    私もちょうどこの壁に^^;

    昔ならGUIからコレクションアクセスしてたんですが、MVVMだとどうすんべ

    返信削除