2011-01-03 10 views
5

複数レイヤーのデスクトップアプリケーションを設計しています。GUIレイヤー(WinForms MVP)はアダプタクラスのインターフェイスへの参照を保持していて、実際の作業を行うBLクラスを呼び出します。C#での非同期呼び出しのデザインパターン

GUIは、GUIからの要求の実行とは別に、GUIがインターフェイスを介してサブスクライブできるイベントも発生させます。たとえば、定期的に変更されるBL内にCurrentTimeというオブジェクトがあり、GUIに変更が反映されている必要があります。

マルチスレッドを伴う二つの問題があります。

  1. は、私は、彼らがGUIをブロックしないように、非同期呼び出し ロジックの一部をする必要があります。
  2. GUIが受信するイベントの一部は、GUI以外のスレッドから発生します。

マルチスレッドはどのレベルで処理するのが最適ですか?私の直感は、発表者がそれに最も適していると言います、そうですか?私が必要とするアプリケーションの例をいくつか教えてもらえますか?プレゼンターがフォームへの参照を保持して、その上で代理人を呼び出すことができるのは意味がありますか?

EDIT:誰かがさらに良い答えを出さない限り、賞金はおそらくヘンリックに行くでしょう。

答えて

5

TaskベースのBLLを、「バックグラウンド操作」として記述できる部分(つまり、UIによって開始され、明確な終了点がある部分)に使用します。Visual Studio Async CTPには、タスクベースの非同期パターン(TAP)を記述する文書が含まれています。このようにBLL APIを設計することをお勧めします(ただし、async/awaitの言語拡張はまだリリースされていません)。

「サブスクリプション」(つまり、彼らはUIによって開始され、無期限に継続している、である)、いくつかのオプションがあります(私の個人的な好みの順番で)あるあなたのBLLの部分については

  1. Taskに準拠したAPIを使用しますが、完了することのないTaskCompletionSourceを使用してください(または、アプリケーションのシャットダウンの一部としてキャンセルするだけで完了します)。この場合、とEventProgress<T>(Async CTP内)を作成することをお勧めします。IProgress<T>は、BLLに進捗状況を報告するためのインターフェイス(進捗イベントを置き換える)を提供し、EventProgress<T>SynchronizationContextのキャプチャを処理して「レポート進捗」デリゲートをマーシャリングします。 UIスレッド。
  2. RxのIObservableフレームワークを使用します。これは設計上の良い一致ですが、かなり急な学習曲線を持ち、個人的に好きなもの(それはリリース前のライブラリです)よりも安定性が劣ります。
  3. 古風なイベントベースの非同期パターン(EAP)を使用して、BLLにSynchronizationContextをキャプチャし、そのコンテキストにキューイングしてイベントを発生させます。

EDIT 2011-05-17:上記を書いているので、非同期CTPチームは、アプローチ(1)(それやや虐待ため、システムを「進行状況をレポート」)、およびRxを推奨されていないと述べていますチームはセマンティクスを明確にするドキュメントをリリースしました。私は現在購読のためにRxをお勧めします。

0

GUIでスレッドを使用し、コントロールをControl.Invoke()で更新することをお勧めします。

GUIアプリケーションでスレッドを使用しない場合は、BackgroundWorkerクラスを使用できます。

ベストプラクティスは、外部からコントロールを更新するためのフォームには、通常パブリックメソッドを使用するロジックがあります。この呼び出しがMainThread以外のスレッドから行われた場合は、control.InvokeRequired/control.Invoke()(ここで、controlは更新対象のコントロール)を使用して、不正なスレッドアクセスを保護する必要があります。

AsynCalculatePi例をご覧ください。多分、それは良い出発点です。

+0

私はControl.Invoke()の動作を知っていますが、質問はデザインパターンとベストプラクティスについてです。どのコントロールをControl.Invoke()に使用しますか?フォームを使用するのがベストプラクティスですか?私はプレゼンターのフォームへの参照が必要であるか、フォームのコードビハインドからInvoke()を呼び出すのですか? –

+0

@IIya:わかりました。状況によって異なりますが、通常はコントロールを更新するロジックを記述し、ターゲットコントロールを使用してInvokeRequiredをチェックし、次にcontrol.Invoke()をチェックします。私の編集を参照してください。 –

1

私は「スレッドプロキシメディエータパターン」と考えます。ここでの例CodeProject

基本的に、アダプタのすべてのメソッド呼び出しはワーカースレッドで実行され、すべての結果はUIスレッドで返されます。

2

これは、作成しているアプリケーションの種類によって異なります。たとえば、バグは受け付けますか?あなたのデータ要件は何ですか?ソフトリアルタイムですか?酸?最終的に一貫性があり、かつ/または部分的に接続された/時折切断されたクライアント?

同時性と非同期性の区別があることに注意してください。 asynchronocityを持つことができ、実際に同時に実行しているプログラムを実際に持たずにメソッド呼び出しインタリーブを呼び出すことができます。

1つのアイデアは、書き込み側が変更されたときにイベントをパブリッシュするアプリケーションの読み取り側と書き込み側を持つことです。これはイベント駆動型システムにつながる可能性があります。読書面は公開されたイベントから構築され、再構築することができます。 UIはタスク駆動型であるため、実行するタスクによってBLが取るコマンド(または必要に応じてドメインレイヤー)が生成されます。

あなたが上記を持っているなら、論理的な次のステップは、イベントソースとなることです。次に、以前にコミットされたものを通して、ライト・モデルの内部状態を再作成します。 CQRS/DDDに関するGoogleグループがあります。

UIの更新に関しては、System.Reactive、System.Interactive、System.CoreExのIObservableインターフェイスが適していることがわかりました。ディスパッチャ - スレッドプールなどのさまざまな並行呼び出しコンテキストをスキップして、タスク並列ライブラリとうまくやり取りします。

あなたはビジネスロジックをどこに置かなければならないかを考慮する必要があります。ドメイン駆動型の場合は、配布するバイナリの更新手順があるのでアプリケーションに入れることができますとにかく、時が来るとアップグレードするが、サーバーに置くという選択肢もある。コマンドは、接続指向のコードが失敗したとき(小さなものとシリアライズ可能で、それらの周りにUIを設計できる)、書き込み側と作業単位の更新を行ううえで便利です。 IObservable.ObserveOnDispatcher(...)に優先順位を追加

あなたの例を与えるために、このコードで、this threadを見て、 - 呼び出し:

public static IObservable<T> ObserveOnDispatcher<T>(this IObservable<T> observable, DispatcherPriority priority) 
    { 
     if (observable == null) 
      throw new NullReferenceException(); 

     return observable.ObserveOn(Dispatcher.CurrentDispatcher, priority); 
    } 

    public static IObservable<T> ObserveOn<T>(this IObservable<T> observable, Dispatcher dispatcher, DispatcherPriority priority) 
    { 
     if (observable == null) 
      throw new NullReferenceException(); 

     if (dispatcher == null) 
      throw new ArgumentNullException("dispatcher"); 

     return Observable.CreateWithDisposable<T>(o => 
     { 
      return observable.Subscribe(
       obj => dispatcher.Invoke((Action)(() => o.OnNext(obj)), priority), 
       ex => dispatcher.Invoke((Action)(() => o.OnError(ex)), priority), 
       () => dispatcher.Invoke((Action)(() => o.OnCompleted()), priority)); 
     }); 
    } 

上記の例を使用することができます{コスト:仮想スターバックスのお店で、たとえば、あなたがイベント「CustomerBoughtCappuccino」を生成「バリスタ」クラスのようなものを持っているドメインエンティティを持っていると思いますので、このようblog entryは...

public void LoadCustomers() 
{ 
    _customerService.GetCustomers() 
     .SubscribeOn(Scheduler.NewThread) 
     .ObserveOn(Scheduler.Dispatcher, DispatcherPriority.SystemIdle) 
     .Subscribe(Customers.Add); 
} 

について説明します。 '$ 3'、タイムスタンプ: '2011-01-03 12:00:03.334556 GMT + 0100 '、...など}あなたの読者は、これらのイベントを購読します。読み込み側は、データを表示する各画面のデータモデルになります。ビューには、観察可能な辞書like thisのビューと同期されるユニークなViewModelクラスがあります。リポジトリは(:IObservable)になり、プレゼンターはそのすべて、またはその一部にサブスクライブします。 - >ユーザの操作に焦点を当て

  • とBL駆動コマンド、非同期
  • はあなたBLのみを取ることを考えると
  • を読み書き偏析駆動

    1. タスク:あなたのGUIが可能であること方法コマンドを実行し、そのディスプレイの上に「すべてのページに適したタイプ」の読取りモデルではない場合は、内部、内部保護、および非公開のほとんどのものを作成できます。つまり、System.Contractsを使用して、それにバグはありません(!)。これは、あなたのリードモデルが読むイベントを生成します。降格された非同期タスク(IAsyncResults)のワークフローのオーケストレーションについては、Caliburn Microの主な原則を採用することができます。

      あなたが読むことができるRx design guidelinesがあります。イベント調達とcqrsに関するcqrsinfo.com非同期プログラミング領域を超えて並行プログラミング領域に移行することに本当に関心があるならば、マイクロソフトはこのようなコードをプログラミングする方法について、無料でwritten bookをリリースしました。

      希望します。

    +0

    ありがとうございます。あなたは私に、このすべてについて読んだだけで月を過ごすのに十分なポインタを与えました:) –

    +0

    私はうれしいです。個人教師が必要な場合は、私にPMを送ってください。私は相談します。 – Henrik

    関連する問題