2

すべて、大規模な 'Cost-Crunching' アルゴリズムをマルチスレッド化するように依頼された状況があります。私は比較的Tasks の経験があり、次のようなパターンを採用することに自信があります

CancellationTokenSource cancelSource = new CancellationTokenSource();
CancellationToken token = cancelSource.Token;
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();

Task<bool> asyncTask = null;
asyncTask = Task.Factory.StartNew<bool>(() =>
    SomeMethodAsync(uiScheduler, token, _dynamic), token);

asyncTask.ContinueWith(task =>
{
    // For call back, exception handling etc.
}, uiScheduler);

そして、私が提供する必要がある操作とUI操作のために、私は使用します

Task task = Task.Factory.StartNew(() =>
{
    mainForm.progressLeftLabelText = _strProgressLabel;
}, CancellationToken.None, 
   TaskCreationOptions.None, 
   uiScheduler);

これがメソッドにラップされる可能性がある場所。

今では、これらすべてをはるかに単純化し、async/await.NET 4.5 のキーワードを活用できることに気付きました。ただし、いくつか質問があります。使用して起動する長時間実行されるメソッドがある場合

// Start processing asynchroniously.
IProgress<CostEngine.ProgressInfo> progressIndicator =
    new Progress<CostEngine.ProgressInfo>();
cancelSource = new CancellationTokenSource();
CancellationToken token = cancelSource.Token;
CostEngine.ScriptProcessor script = new CostEngine.ScriptProcessor(this);
await script.ProcessScriptAsync(doc, progressIndicator, token);

whereCostEngine.ProgressInfoは進捗情報を返すために使用されるいくつかの基本的なクラスであり、メソッドProcessScriptAsyncは次のように定義されています

public async Task ProcessScriptAsync(SSGForm doc, IProgress<ProgressInfo> progressInfo,
                                     CancellationToken token, bool bShowCompleted = true)
{
    ...
    if (!await Task<bool>.Run(() => TheLongRunningProcess(doc)))
        return
    ...
}

2 つの質問があります。

  1. ほとんどすぐに UI に制御を戻すために、新しいデリゲートProcessScriptAsyncを待機します (これにより、/の無限のチェーンを回避しているように見えます)。この呼び方は正しいですか?[「遅延初期化」、外側のメソッドでラップすることによって?]Task<bool>asyncawaitProcessScriptAsync

  2. 内から UI にアクセスするには、UI をTheLongRunningProcess渡すだけですかTaskScheduler uiScheduler。つまりTheLongRunningProcess(doc, uiScheduler)、次を使用します。


Task task = Task.Factory.StartNew(() =>
{
    mainForm.progressLeftLabelText = _strProgressLabel;
}, CancellationToken.None, 
   TaskCreationOptions.None, 
   uiScheduler);

従来通り?

長々と申し訳ありませんが、お時間をいただきありがとうございます。

4

2 に答える 2

2
  1. 場合によります。多くのコードを示しましたが、実際に質問していることを 1 つ省略しています。まず、コードが何であるかを知らなければ、実際に時間がかかるかどうかを知ることはできません。次に、既に完了したタスクを待機すると、これが認識され、継続をスケジュールせずに続行します (タスクのスケジュールには時間がかかるため、これは最適化です)。待機しているタスクが完了していない場合でも、呼び出し側で継続が実行されSynchronizationContext、UI スレッドが再びビジー状態になります。ConfigureAwait(false)ただし、継続がスレッドプールで実行されるようにするために使用できます。これは両方の問題を処理する必要があります。...これを行うと、セクションのUI コントロールにアクセスできなくなることに注意してください。ProcessScriptAsync(特別なことは何もしません)。また、ProcessScriptAsync現在はスレッド プール スレッドで実行されているTask.Runため、メソッド呼び出しをバックグラウンド スレッドに移動するために を使用する必要がないことにも注意してください。

  2. それは1つのオプションです、はい。ただし、進行状況に基づいて UI を更新する場合は、それIProgressが目的です。すでに使用されているようですので、これを行うための望ましいモデルです。これが、渡している既存のものとは別の種類の進行状況を更新している場合(つまり、int としての完了率ではなくIProgress、ステータステキスト)、秒を渡すことができます。

于 2013-01-22T18:45:43.817 に答える
1

バックグラウンド スレッド (CPU を集中的に使用する操作またはサポートされていない IO 操作の場合) と UI スレッド (UI コントロールを操作する場合) を切り替えようとするasyncことは、多くの場合、設計が悪いことを示していると思います。計算と UI コードは分離する必要があります。

何らかの進行状況を UI に通知するためだけにこれを行っている場合は、 を使用しますIProgress<T>。スレッド間のマーシャリングは、そのインターフェイスの実装の責任となり、 を使用できますProgress<T>。これは、SynchronizationContext.

バックグラウンド スレッド コードと UI スレッド コードの混合を避けることができず、UI 作業が進行状況レポートではない (IProgress<T>適合しない) 場合は、おそらくバックグラウンド スレッド コードの各ビットを独自の で囲みawait Task.Run()、UI コードを残します。トップレベル。

シングルを使用してバックグラウンド スレッド コードを実行し、次にwithをTask.Run()使用して UI スレッドに切り替えるというソリューションも機能します。その場合、特にUI コードでも使用したい場合は、いくつかのヘルパー メソッドが役立つ場合があります。(そうしないと、 の結果を 2 倍にすることを覚えておく必要があります)StartNew()uiSchedulerawaitawaitStartNew()

さらに別のオプションはSwitchTo(TaskScheduler)、指定されたスケジューラで継続するカスタム awaiter を返すメソッドを作成することです。このようなメソッドは一部の非同期 CTP にありましたが、例外処理に関して危険すぎると見なされたため、削除されました。

于 2013-01-22T21:11:27.037 に答える