Task.ContinueWith は、TaskScheduler.Current を引数としていつ呼び出されるべきですか? 質問する

Task.ContinueWith は、TaskScheduler.Current を引数としていつ呼び出されるべきですか? 質問する

私たちは使用していますこのコードスニペットStackOverflow から、タスクのコレクションの最初のタスクが正常に完了するとすぐに完了する Task を生成する方法を紹介します。実行の非線形性のため、これは実際には実行可能ではないため、このコードでは代わりに TaskSchedulerasync/awaitを使用します。ただし、ContinueWith()番号 ソース言及した方法は、ほとんどの開発者が継続からTaskScheduler.Current通常期待する動作を使用するため、危険になる可能性があります。TaskScheduler.Default

一般的な考え方としては、常に明示的な TaskScheduler を ContinueWith に渡す必要があるようです。ただし、異なる TaskScheduler がどのような場合に最も適切であるかについての明確な説明は見たことがありません。

ではなく に移行TaskScheduler.Currentするのが最善である具体的な例は何ですか? この決定を行う際に従うべき経験則はありますか?ContinueWith()TaskScheduler.Default

コンテキストとして、私が参照しているコード スニペットを以下に示します。

public static Task<T> FirstSuccessfulTask<T>(IEnumerable<Task<T>> tasks)
{
    var taskList = tasks.ToList();
    var tcs = new TaskCompletionSource<T>();
    int remainingTasks = taskList.Count;
    foreach(var task in taskList)
    {
        task.ContinueWith(t =>
            if(task.Status == TaskStatus.RanToCompletion)
                tcs.TrySetResult(t.Result));
            else
                if(Interlocked.Decrement(ref remainingTasks) == 0)
                    tcs.SetException(new AggregateException(
                        tasks.SelectMany(t => t.Exception.InnerExceptions));
    }
    return tcs.Task;
}

ベストアンサー1

おそらく、実行中のデリゲート インスタンスが実行するアクションに適したタスク スケジューラを選択する必要があります。

次の例を検討してください。

Task ContinueWithUnknownAction(Task task, Action<Task> actionOfTheUnknownNature)
{
    // We know nothing about what the action do, so we decide to respect environment
    // in which current function is called
    return task.ContinueWith(actionOfTheUnknownNature, TaskScheduler.Current);
}

int count;
Task ContinueWithKnownAction(Task task)
{
    // We fully control a continuation action and we know that it can be safely 
    // executed by thread pool thread.
    return task.ContinueWith(t => Interlocked.Increment(ref count), TaskScheduler.Default);
}

Func<int> cpuHeavyCalculation = () => 0;
Action<Task> printCalculationResultToUI = task => { };
void OnUserAction()
{
    // Assert that SynchronizationContext.Current is not null.
    // We know that continuation will modify an UI, and it can be safely executed 
    // only on an UI thread.
    Task.Run(cpuHeavyCalculation)
        .ContinueWith(printCalculationResultToUI, TaskScheduler.FromCurrentSynchronizationContext());
}

継続デリゲート インスタンスはスレッド プール上で安全に実行できるため、これはおそらくFirstSuccessfulTask()を使用できる例です。TaskScheduler.Default

カスタムタスクスケジューラを使用して、ライブラリにカスタムスケジューリングロジックを実装することもできます。たとえば、スケジューラOrleans フレームワークの Web サイトのページ。

詳細については以下をご確認ください:

おすすめ記事