0

非同期プログラミングがより自然になります。
C# が async/await をサポートするようになりました。Anders Hejlsberg がここで
述べた ように、これらすべてにより、レスポンシブなアプリケーションを作成できます。私たちのチームも非同期が好きです。しかし、.net v.4.0 用にコーディングしているため、便宜上いくつかのヘルパー メソッドを開発する必要がありました。

しかし、非同期操作を使用すればするほど、それは難しくなります。
ほんの一例:

GUI のマスター詳細部分があります。ユーザーがマスターアイテムをクリックすると、かなり長い操作が詳細を取得し始めます。この操作は、db リクエストやファイルのダウンロードのように、本質的に止められないことがあります。そのため、UI の応答性を維持するために、この長い操作を非同期で開始します。
ここで、マスター アイテム リストをどうするかを決める必要があります。
ユーザーがアイテムを変更して新しい操作を開始できるようにすることができます。すでに実行中の操作の結果は、すぐに破棄するか、キャッシュに入れることができます。
そして、ユーザーが item1、item2、そして item1 をもう一度クリックしたとします。彼がこれをかなり迅速に行うと、item1 の既に開始されていてキャンセルされていない操作がまだ完了していない可能性があります。この場合、新しい操作を開始するよりも、この操作の結果を待つ方がよいでしょう。したがって、現在実行中の操作のキャッシュが存在する必要があります。(await を使用してこれを簡単に行う方法がわかりません)
確かに、操作が完了するまでマスター項目リストを無効にすることはできますが、UI の他の部分はライブのままですが、あまりユーザーフレンドリーではありません。

2 番目の例 (最初のものへのアップグレード):
GUI の詳細部分は 2 つのリストで構成されています。各リストの内容は、選択したマスター アイテムによって異なります。それに加えて、一方のリストでの選択が他方のリストの状態を変更するという方法で、リストは互いに影響します。(はい、非常に複雑なユースケースです)。
では、両方のリストのデータを非同期で取得したい場合はどうすればよいでしょうか?
どのリストが競争に勝ち、そのデータを最初に取得するかは事前にはわかりません。これは本当に問題ではありません。重要なのは勝者の存在そのものです。リスト 1 が完了し、ユーザーが選択を変更します...しかし、この選択が、まだ入力されていないリスト 2 の状態に影響するのを待ちます。ここでも、両方のリストがロードされる前に選択を禁止できますが、ユーザーがリストに対して実行できる独立した操作は多数あります。したがって、これは選択ではありません。
わかりました、どうすればいいですか?さて、両方のリストがロードされたときに開始されるタスクを紹介できます。このタスクでは、現在の選択を取得し、必要に応じて状態を設定できます。

そして、ポイントは何ですか?一般的な
並列プログラミングについて読んだことがありますが、ユーザーの操作によって GUI の状態が (実行フローの観点から) ランダムに変更されるため、GUI は非常に特殊なケースであると考えています。GUI 関連の設計パターン (MVC、MVP、MVVM) があるため、特別な並列パターンが必要です。

これが私の質問です:

一般的な非同期 GUI タスクに対応する専用の並列パターンは存在しますか?

PSこの質問がprogrammers.stackexchangeにより適していると思われる場合は、自由に移行してください。ありがとうございました。

4

1 に答える 1

1

デスクトップ プログラムを非同期に変換するときに UI を再考することは珍しくありません。非同期プログラムは、対応する同期プログラムよりも多くの可能な状態を持つことができます。

いくつかの便利なパターンがあります: コントロールを無効にし (または、同等に "Loading..." 記号でそれらを覆います)、状態を "コンテキスト" に保ちます (これについては以下で説明します)。操作のキューを維持することもできますが、操作のキューを管理するための UI を構築するのは簡単ではないため、ほとんどの人は気にしません。

この操作は、db リクエストやファイルのダウンロードのように、本質的に止められないことがあります。

(補足:どちらも本質的に止められないものではありません)

ユーザーがアイテムを変更して新しい操作を開始できるようにすることができます。すでに実行中の操作の結果は、すぐに破棄するか、キャッシュに入れることができます。

アプリケーションと操作の種類に応じて、これらはどちらも合理的なアプローチです。

そして、ユーザーが item1、item2、そして item1 をもう一度クリックしたとします。彼がこれをかなり迅速に行うと、item1 の既に開始されていてキャンセルされていない操作がまだ完了していない可能性があります。この場合、新しい操作を開始するよりも、この操作の結果を待つ方がよいでしょう。したがって、現在実行中の操作のキャッシュが存在する必要があります。

病的なユーザー シナリオ向けの設計はお勧めしません。この場合、ユーザーが実際にそのマスター リストを驚くべき速さでクリックしている場合は、パフォーマンスの低下を許容してください。本当に心配な場合は、(前述のように)結果をキャッシュして、 item1 の再実行が非常に高速になるようにすることができます。結果のキャッシュで十分です。実行中の操作のキャッシュは必要ありません。

確かに、操作が完了するまでマスター アイテム リストを無効にすることはできますが、UI の他の部分は有効なままですが、あまりユーザー フレンドリーではありません。

心に留めておくべきことの 1 つは、同期アプリケーションと同じくらいユーザーフレンドリーではないということです。同期アプリケーションは、(おそらく) この間応答しなくなります。

各リストの内容は、選択したマスター アイテムによって異なります。それに加えて、一方のリストでの選択が他方のリストの状態を変更するという方法で、リストは互いに影響します。

UI を再考する必要があるかもしれません。この複合体が本当に必要ですか?別の方法で考えてみてください。これほど複雑な UI を持つ公開アプリケーションを使用していませんか? 彼らは代わりに何をしますか?

そうは言っても、私のブログで説明している古い非同期プログラミングのトリックがあります:非同期コールバック コンテキストです。基本的に、「Cookie」を使用して「現在の」状態を定義するという考え方です(一部のスコープについて)。状態が変わると、「Cookie」を変更します。そのスコープの一部であるすべての非同期メソッドは、Cookie を監視し、変更時に特別なアクションを実行できます。

object(私のブログで説明しているように)Cookie に使用できますが、以下も使用できCancellationTokenます。

private CancellationTokenSource masterListSelectionCookie;
private Task list1Download;
private Task list2Download;

void MasterList_Click(...)
{
  // Change the cookie, canceling any previous one.
  if (masterListSelectionCookie != null)
    masterListSelectionCookie.Cancel();
  masterListSelectionCookie = new CancellationTokenSource();

  // Clear out both lists.
  list1.Items.Clear();
  list2.Items.Clear();

  // Start both lists downloading.
  list1Download = DownloadList1Async();
  list2Download = DownloadList2Async();
}

async void List1_Click(...)
{
  // Get a local copy of the current cookie.
  var localMasterListSelectionCookie = masterListSelectionCookie.Token;

  // Ensure list2 is done downloading.
  await list2Download;

  // If the cookie has changed, ignore the click.
  if (localMasterListSelectionCookie.IsCancellationRequested)
    return;

  // Apply the click changes to list2 items.
  FilterList2(list1.Item);
}

async void List2_Click(...)
{
  // Get a local copy of the current cookie.
  var localMasterListSelectionCookie = masterListSelectionCookie.Token;

  // Ensure list1 is done downloading.
  await list1Download;

  // If the cookie has changed, ignore the click.
  if (localMasterListSelectionCookie.IsCancellationRequested)
    return;

  // Apply the click changes to list1 items.
  FilterList1(list2.Item);
}

考え方としては、Cookie に暗黙的に含まれる「状態」に敏感な操作ごとawaitに、Cookie が変更されたかどうか (つまり、CancellationTokenキャンセルされたかどうか) を毎回確認し、適切な動作を行う (この場合は、他のリストをフィルタリングします)。

于 2013-01-22T00:50:20.230 に答える