none
スレッドセーフなObservableCollection RRS feed

  • 質問

  • こんにちは倉田 智朗です。

    ObservableCollection<hoge>とListViewをXAMLでバインディングさせているんですが、今作成しているプログラムはObservableCollection<hoge>を別スレッドから頻繁にアクセスします。

    実行したらやっぱりDispatherつかえーとのエラーが^^;

    かたっぱしからDispather使うしか無いのでしょうか?

    2011年7月11日 22:48

回答

  • 最終的な目的はUIスレッドで動かすことですので、Dispatcherを使うしか無いと思います。BackgroundWorkerも使えますが、結局のところ裏でDispatcherを使用しています。

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年7月12日 0:41
    モデレータ
  • INotifyPropertyChanged によって通知するオブジェクトについては、暗黙に Dispatcher が使われるので明示的な操作は必要ありません。自由に外部スレッドから操作でき、自動的にディスパッチされて UI スレッド上で UI に反映されます。

    INotifyCollectionChanged なコレクションに格納されている INotifyPropertyChanged オブジェクトがどうだったかは覚えてません。


    2011年7月12日 8:13
  • > たとえばバインディングしているプロパティーの値を変えるとき

    最初に書いていますが、アプリケーションのロジックとして Dispatcher を経由する必要がないならば、プロパティの変更を自由に行うことができます。ただし、当然ですが、通常のスレッドセンシティブなプロパティ変更(これはDispatcherを経由する必要がある)と、別スレッドからアクセスできるスレッドセーフなプロパティ変更をきちんと使い分ける必要があります。この区別がアプリケーションのロジック上からきちんとできないと、例外が飛んだり変更通知が飛ばなかったりすることになります。

     

    public int Property1
    {
      get { return /* Property1 の取得処理 */; }
      set
      {
        // UI からの呼び出しまたは、安全な呼び出しの場合
        if (Dispatcher.CheckAccess() || this.CanModifyDirectly)
        {
          // 直接、プロパティを書き換える
        }
        else
        {
          // スレッドセーフではない呼び出しは Dispatcher へ
          Dispatcher.Invoke(
            DispatcherPriority.xxx,
            x => this.Property1 = x,
            value);
        }
      }
    }
    
    // アプリケーションロジックとして Dispatcher を経由しないでも
    // 安全に書き換えることが可能であるかどうかを判定する
    protected bool CanModifiyDirectly
    {
      get { return false; }
    }
    
    

     

    2011年7月12日 8:59

すべての返信

  • 最終的な目的はUIスレッドで動かすことですので、Dispatcherを使うしか無いと思います。BackgroundWorkerも使えますが、結局のところ裏でDispatcherを使用しています。

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年7月12日 0:41
    モデレータ
  • trapemiyaさん、いつもお返事ありがとうございます。Dispatherしかやはり手がないのですね。ということは、Hogeクラスのプロパティーを変える時もDispather経由でしょうか。むーん、正直冗長な気がするのでもうちょっと簡単な手法を用意して欲しいですね^^;

    2011年7月12日 1:48
  • 別スレッドからのアクセスが、アプリケーション的に安全であることが保障されるのであれば、派生クラスを使用してそういった安全なアクセスに限定した横道を用意してあげればよいかと思います。

    安全であることを保障できないか、安全であることをカプセル化したいのであれば、Dispacher 経由になります。

    2011年7月12日 3:57
  • K.Takaokaさんお返事ありがとうございます。今回のケースではHogeクラスをスレッドセーフにするのは難しくないのですが。たとえばバインディングしているプロパティーの値を変えるときやはりDispacherの使用がいるのでしょうか?

    って、Dispacherの綴り間違えていましたね、失礼しました。

    2011年7月12日 4:54
  • って、Dispacherの綴り間違えていましたね、失礼しました。

    まだ違っているかも・・・。私も修正しときました。aを抜かしてました・・・

    さて、Hogeクラスのスレッドセーフですが、私の見解ではDispatcherの使用が必要です。以下は読まれましたでしょうか?(先に書けばよかったですね)

    Dispatcher を使用して応答性の高いアプリケーションを構築する
    http://msdn.microsoft.com/ja-jp/magazine/cc163328.aspx

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年7月12日 7:21
    モデレータ
  • INotifyPropertyChanged によって通知するオブジェクトについては、暗黙に Dispatcher が使われるので明示的な操作は必要ありません。自由に外部スレッドから操作でき、自動的にディスパッチされて UI スレッド上で UI に反映されます。

    INotifyCollectionChanged なコレクションに格納されている INotifyPropertyChanged オブジェクトがどうだったかは覚えてません。


    2011年7月12日 8:13
  • > たとえばバインディングしているプロパティーの値を変えるとき

    最初に書いていますが、アプリケーションのロジックとして Dispatcher を経由する必要がないならば、プロパティの変更を自由に行うことができます。ただし、当然ですが、通常のスレッドセンシティブなプロパティ変更(これはDispatcherを経由する必要がある)と、別スレッドからアクセスできるスレッドセーフなプロパティ変更をきちんと使い分ける必要があります。この区別がアプリケーションのロジック上からきちんとできないと、例外が飛んだり変更通知が飛ばなかったりすることになります。

     

    public int Property1
    {
      get { return /* Property1 の取得処理 */; }
      set
      {
        // UI からの呼び出しまたは、安全な呼び出しの場合
        if (Dispatcher.CheckAccess() || this.CanModifyDirectly)
        {
          // 直接、プロパティを書き換える
        }
        else
        {
          // スレッドセーフではない呼び出しは Dispatcher へ
          Dispatcher.Invoke(
            DispatcherPriority.xxx,
            x => this.Property1 = x,
            value);
        }
      }
    }
    
    // アプリケーションロジックとして Dispatcher を経由しないでも
    // 安全に書き換えることが可能であるかどうかを判定する
    protected bool CanModifiyDirectly
    {
      get { return false; }
    }
    
    

     

    2011年7月12日 8:59
  • みなさま、お返事ありがとうございます。

     

     public string FileName

            {

                set            

                {                

                    System.Windows.Threading.Dispatcher d = System.Windows.Application.Current.Dispatcher;

                    if (d.CheckAccess())

                    {

                        this.fileName = value;

                        OnPropertyChanged("FileName");

                    }

                    else

                    {

                        d.Invoke(new MethodInvoker(delegate

                        {

                            FileName = value;

                        }));

                    }

                }

                get

                {                            

                    return this.fileName;                

                }

            }

     

    こんな感じで動いてくれました!。ラムダ式がわからないのでこのような書き方に^^;

    ところで、モデルが階層になっているときは、その間のプロパティーにもOnPropertyChangedやってやらないといけませんね、こんなところではまりました^^;

     

     

    >INotifyPropertyChanged によって通知するオブジェクトについては、暗黙に Dispatcher が使われるので明示的な操作は必要ありません。自由に外部スレッドから操作でき、自動的にディスパッチされて UI スレッド上で UI に反映されます。

    >INotifyCollectionChanged なコレクションに格納されている INotifyPropertyChanged オブジェクトがどうだったかは覚えてません。

     

    これは後々テストしてみたいです。

    無事趣味のフリーソフトの開発が進みました、皆様ありがとうございました。

    2011年7月13日 4:07
  • > こんな感じで動いてくれました!

    CheckAccess() のみだと、別スレッドからの操作はすべて dispatcher 経由になってしまいます。もちろん、それで問題になることはないのですが、バックグラウンドからの操作を UI に通知しないで更新することで、並列化を図っているのだとしたら、この実装ではまったく並列化できていないことになりますので、注意してください。

     

    2011年7月18日 4:08
  • お返事ありがとうございます。

    なるほどDispatcher経由だけだと並列化にならないですね^^;

    処理速度とかも考慮すると、CanModifiyDirectlyのような実装を考えないといけないですね。


    2011年7月22日 14:20