0

長時間実行される OperationA と、はるかに高速な OperationB の 2 つの操作があります。私はTAPを使用してそれらを並行して実行し、両方が終了すると結果を返しました:

var taskA = Task.Factory.StartNew(()=>OperationA());
var taskB = Task.Factory.StartNew(()=>OperationB());
var tasks = new Task[] { taskA, taskB };
Task.WaitAll(tasks);
// processing taskA.Result, taskB.Result

ここには魔法はありません。ここでやりたいことは、OperationA がまだ実行されている場合に備えて、OperationB が無期限に終了したときに繰り返すことです。したがって、操作 A が終了し、操作 B の最後のパスが終了したときに、手順全体の終了点が発生します。可能であれば、while ループで OperationA の Status をポーリングする必要のない、ある種の効果的なパターンを探しています。このPluralsight コースで提案されている WaitAllOneByOne パターンまたは同様のものの改善を目指しています。

4

2 に答える 2

0

あなたのアプローチを基礎として、少し適応させました:

var source = new CancellationTokenSource();
var token = source.Token;
var taskA = Task.Factory.StartNew(
    () => OperationA()
    );
var taskAFinished = taskA.ContinueWith(antecedent =>
    {
        source.Cancel();
        return antecedent.Result;
    });

var taskB = Task.Factory.StartNew(
    () => OperationB(token), token
    );
var taskBFinished = taskB.ContinueWith(antecedent =>
    {
        switch (antecedent.Status)
        {
            case TaskStatus.RanToCompletion:
            case TaskStatus.Canceled:
                try
                {
                   return ant.Result;
                }
                catch (AggregateException ae)
                {
                   // Operation was canceled before start if OperationA is short
                   return null;
                }
            case TaskStatus.Faulted:
                return null;
        }
        return null;
    });

それぞれの操作の結果を返す 2 つの継続を作成したので、両方が完了するのを待つことができました (2 番目の操作でのみ実行しようとしましたが、うまくいきませんでした)。

var tasks = new Task[] { taskAFinished, taskBFinished };
Task.WaitAll(tasks);

1 つ目は先行タスクの結果をさらに渡すだけで、2 つ目は OperationB のこの時点で利用可能な集計結果を取得します (RanToCompletion と Canceled の両方のステータスは、プロセスの正しい終了と見なされます)。OperationB は次のようになります。

public static List<Result> OperationB(CancellationToken token)
{
    var resultsList = new List<Result>();
    while (true)
    {               
        foreach (var op in operations)
        {
            resultsList.Add(op.GetResult();
        }
        if (token.IsCancellationRequested)
        {
            return resultsList;
        }
    }
}

ロジックを少し変更 - OperationB 内のすべてのループは単一のタスクと見なされるようになりましたが、これは、それらをアトミックに保ち、各実行から結果を収集する何らかの調整プリミティブを記述するよりも簡単です。どのループがどの結果を生成したかをあまり気にしない場合、これはまともな解決策のようです。必要に応じて、後でより柔軟な実装に改善される可能性があります (私が実際に探していたのは、複数の操作を再帰的にチェーンすることです。OperationB 自体は、内部で同じ動作をする OperationC の繰り返しを小さくすることができます。OperationC - C がアクティブなときに実行されている複数の OperationD など)。
edit
OperationA が迅速で、OperationB が開始される前にキャンセルが発行された場合に備えて、taskBFinished に例外処理を追加しました。

于 2013-08-06T10:22:36.503 に答える