2009-03-02 11 views
10

私は現在、winformsデータバインディングとバックグラウンドスレッドからの更新(100を超えるレコードで1秒に1回)を頻繁に使用するアプリケーションのデータバインディング部分を設計/改訂しています。WinFormsマルチスレッドデータバインディングシナリオ、ベストプラクティス?

アプリケーションが株式取引アプリケーションであり、バックグラウンドスレッドがデータ変更を監視し、それらをデータオブジェクトに配置するとします。これらのオブジェクトはBindingList<>に格納され、INotifyPropertyChangedを実装して、変更をデータバインドを介してwinformsコントロールに伝播します。 さらに、データオブジェクトは現在、WinformsSynchronizationContext.Sendを介してUIスレッドに変更をマーシャリングしています。 ユーザーはUIの一部の値を入力することができます。つまり、値の一部を両側から変更することができます。ユーザーの値は更新によって上書きされるべきではありません。

だから私の心に来て、いくつか質問があります。

  • は、一般的な設計・ギルドラインは、(データバインディングのバックグラウンド更新)ことを行う方法ありますか?
  • UIスレッドでマーシャリングする方法とタイミングは?
  • バインディング/データオブジェクトと対話するバックグラウンドスレッドの最良の方法は何ですか?
  • どのクラス/インターフェイスを使用する必要がありますか? (のBindingSource、...)
  • ...

UIは本当にコントロールの更新をバックグラウンドスレッドがあることを知りませんし、データバインディングのシナリオで私の理解のようUI「はshouldnデータがどこから来ているかを知っている...バックグラウンドスレッドはUIにデータをプッシュするものだと考えることができるので、バックグラウンド作業者が私が探しているオプションであるかどうかは分かりません。

場合によっては、データ/ビジネスオブジェクトの操作中にUI応答を取得したいことがあります(たとえば、再計算中に背景を設定するなど)。バックグラウンドにバインドされているステータスプロパティでプロパティを変更するだけでは、計算が完了した後にコントロールが再描画されるため、十分ではありません。私の考えは、プロパティ変更されたイベントをフックし、コントロールの.update()を呼び出すことです... それについての他のアイデア?

答えて

6

ほとんどの "ソリューション"がカスタムコードを大量に持ち、BeginInvoke()またはSystem.ComponentModel.BackgroundWorker(それ自体はBeginInvokeを超える薄いラッパーです)。

以前は、データが安定するまですぐにINotifyPropertyChangedイベントの送信を遅らせたいということも発見しました。 1つの適切な変更イベントを処理するコードは、しばしば他のプロパティを読み取る必要があります。また、多くのプロパティの状態が変わるたびに、自分自身を再描画する必要があるコントロールがあり、コントロールをあまり頻繁に再描画しないようにする必要があります。

まず、各カスタムのWinFormsコントロールは、それがWM_PAINTOnPaint)メッセージだったとき、それはすべてのデータオブジェクトをロックする必要はありませんので、PropertyChangedイベントハンドラで自分自身をペイントするために必要なすべてのデータを読み込む必要があります。コントロールは新しいデータを取得したときにすぐに再描画されるべきではありません。代わりにControl.Invalidate()を呼び出す必要があります。 WindowsはWM_PAINTメッセージを可能な限り少ない要求にまとめ、UIスレッドに何もする必要がない場合にのみメッセージを送信します。これにより、再描画の回数とデータオブジェクトがロックされる時間が最小限に抑えられます。

データオブジェクトは、変更が行われたときに何が変更されたかを記録し、変更が完了したらUIスレッドをSendChangeEventsに「キック」させる必要があります。メソッドを呼び出すと、変更されたすべてのプロパティのPropertyChangedイベントハンドラ(UIスレッド上)が呼び出されます。 SendChangeEvents()メソッドが実行されている間、バックグラウンドスレッドの更新を停止するには、データオブジェクトをロックする必要があります。

アップデートのセットがデータベースからBeanを読み取るたびに、UIスレッドはBeginInvokeを呼び出すと「キック」することができます。 UIメッセージキューが空のときにWindowsがWM_TIMERメッセージを送信するだけなので、UIを使用する方がタイマーを使用してUIスレッドをポーリングするほうがよく、UIの応答性が向上します。

また、データバインディングを一切使用せず、UIが各データオブジェクトに「変更されたもの」を要求するように、タイマーが起動することを検討します。データバインディングは常に素敵に見えますが、ソリューションの一部ではなく、すぐに問題の一部になります。

データオブジェクトのロック/アンロックが苦痛で、データベースからの更新を十分に読み込めない可能性があるため、UIスレッドにデータオブジェクトの(仮想)コピーを渡すことをお勧めします。データオブジェクトを変更すると、現在のデータオブジェクトを変更するのではなく、新しいデータオブジェクトを返すように、データオブジェクトを永続的または不変にすることができます。

永続オブジェクトは非常に遅いですが、そうである必要はありません。いくつかのポインタについてはthisthatを参照してください。また、スタックオーバーフローでthisthatを見てください。

retlang - Message-based concurrency in .NETもご覧ください。メッセージのバッチ処理が便利な場合があります。

(WPFの場合は、UIスレッドを設定して、バックグラウンドスレッドによってマルチスレッドモデルから 'バッチ'で更新されます)しかし、WPFはデータバインドイベントはWinFormsです。)

2

そのトピックに固有のMSDN articleがあります。しかし、VB.NETを見るために準備してください。 ;)

さらに、一般的な2番目のスレッドではなく、System.ComponentModel.BackgroundWorkerを使用することもできます。なぜなら、記述するスポーンされたバックグラウンドスレッドとのやりとりをうまく正式化するからです。 MSDNライブラリで与えられた例はかなりまともですので、それを使ってどのように使用するかのヒントを見てください。

編集: 注意:ProgressChangedイベントを使用してUIスレッドに返信する場合、マーシャリングは必要ありません。バックグラウンドスレッドは、UIと通信する必要があるときはいつでもReportProgressを呼び出します。そのイベントに任意のオブジェクトを添付することができるため、手動マーシャリングを行う理由はありません。進捗状況は別の非同期操作を介して伝達されるため、UIが進捗イベントを処理する速度や、イベント終了を待ってバックグラウンドスレッドが中断されることを心配する必要はありません。

バックグラウンドスレッドが進捗状況の変更イベントを速すぎると証明した場合は、Pull vs. Push models for UI updatesをAyendeの優れた記事で見たいと思うかもしれません。

+1

System.ComponentModel.BackgroundWorker、ソリューションの一部であってもよい、しかし、ハードの問題は、UIスレッドを見ずに高速なデータ更新を維持する方法です。スレッドの呼び出しをロックまたはクロスすることについて考えなくても、1行おきにすることなく、上記のことを行うことができます。 –

+0

Backgroundworkerを見ると、スレッドの作成に大きな違いはなく、WindowsFormsSynchronizationContextでマーシャリングすると、BWは同じことを行います。 –

+0

レスポンスの編集を参照してください。 – Dun3

0

これは私がUpdate Controlsに解決した問題です。私はあなたのコードを書き直すことを示唆するのではなく、アイデアを見るための何らかの情報源を提供するためにこれを持っていきます。

私がWPFで使用した手法は、Dispatcher.BeginInvokeを使用してフォアグラウンドスレッドに変更を通知することでした。あなたはControl.BeginInvokeでWinformsで同じことをすることができます。残念ながら、Formオブジェクトへの参照をデータオブジェクトに渡す必要があります。

これで、PropertyChangedを起動するBeginInvokeにアクションを渡すことができます。例:

_form.BeginInvoke(new Action(() => NotifyPropertyChanged(propertyName)))); 

データオブジェクトのプロパティをスレッドセーフにする必要があります。

2

はいすべての書籍は、あなたがそれだけ

A UIのためのまともなテストを行うことができ、多くの場合、整理するのは難しいなど完全に正しいですが、それはコードに痛みをすることができ、そして、どのスレッドの構造を示すとなどを呼び出しますパフォーマンスは決して問題ではなく、ポーリングはうまくいくでしょう。

私は、バックグラウンドスレッドのプールによって継続的に更新されているオブジェクトグラフを使いたいと思います。彼らはデータ値の実際の変更をチェックし、実際の変更を確認すると、オブジェクトグラフのルート(または各主項目の意味合い)を更新し、値を更新します。

あなたのフォアグラウンドプロセス(デフォルトではUIスレッドと同じですが)バージョンカウンタをチェックしてバージョンカウンタをチェックし、それがロックされていれば(部分的な更新を停止するため)、ディスプレイをリフレッシュします。

この簡単なテクニックUIスレッドをバックグラウンドスレッドから完全に隔離します

+0

私はタイマーが過去にうまく機能することを発見しました。 –

1

私はちょうど同様の状況 - BeginInvokes経由でUIを更新するbadkgroundスレッドと戦っています。バックグラウンドはすべてのループで10msの遅延がありますが、私は問題が発生しましたが、UIのアップデートがそのループで毎回起動され、アップデートの頻度に追いつくことができず、アプリケーションが効果的に機能しなくなります(何が起こったのか分かりません)スタックを吹き飛ばしました。

私は、呼び出しの上に渡されたオブジェクトにフラグを追加しました。これは単なる準備完了フラグでした。呼び出しを呼び出す前にこれをfalseに設定してから、このフラグをtrueに戻すまで、bgスレッドはUiの更新をそれ以上行わないでしょう。 UIスレッドは画面の更新などを行い、このvarをtrueに設定します。

これにより、bgスレッドは処理を続けることができましたが、それ以上の準備が整うまで、uiはフローをシャットダウンできました。

+0

これではわからない部分だけが、フラグが両方のスレッドからアクセス可能になっている理由です。しかしそれはちょうどうまくいくようです。 –

0

新しいUserControlを作成し、コントロールを追加してフォーマットし(おそらくdock = fill)、プロパティを追加します。 これで、任意のスレッドをプロパティフォームに変更するたびに、usercontrolを呼び出して要素を更新するようにプロパティを設定するようになりました。私の解決策のthats

:私のフォーム上の

private long value; 
    public long Value 
    { 
     get { return this.value; } 
     set 
     { 
      this.value = value; 

      UpdateTextBox(); 
     } 
    } 

    private delegate void Delegate(); 
    private void UpdateTextBox() 
    { 
     if (this.InvokeRequired) 
     { 
      this.Invoke(new Delegate(UpdateTextBox), new object[] {}); 
     } 
     else 
     { 
      textBox1.Text = this.value.ToString(); 
     } 
    } 

私は私の見解をバインド

viewTx.DataBindings.Add(new Binding("Value", ptx.CounterTX, "ReturnValue")); 
0

この投稿は古いですが、私は他の人に選択肢を与えるだろうと思っていました。非同期プログラミングとWindowsフォームのデータバインディングを開始すると、Bindingsourceのデータソースを更新したり、Windowsフォームコントロールにバインドされたリストを更新する際の問題が発生するようです。 wintellectのpowerthreadingツールからJeffrey Richters AsyncEnumeratorクラスを使ってみるつもりです。

理由: 1。彼のAsyncEnumeratorクラスはバックグラウンドスレッドを自動的にUIスレッドにマーシャリングし、同期コードと同様にコントロールを更新することができます。 2. AsyncEnumeratorは、非同期プログラミングを簡素化します。これは自動的に実行されるため、コードは同期的に書き込まれますが、コードはまだ非同期で実行されています。

Jeffrey Richterには、AsyncEnumeratorについて説明しているChannel 9 MSDNのビデオがあります。

私は運がいいです。

-R