什么时候应使用TaskScheduler.Current作为参数调用Task.ContinueWith? [英] When should Task.ContinueWith be called with TaskScheduler.Current as an argument?

查看:243
本文介绍了什么时候应使用TaskScheduler.Current作为参数调用Task.ContinueWith?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在使用StackOverflow中的此代码段来生成一个Task,该Task会在第一个集合的第一个完成任务成功完成.由于其执行具有非线性特性,因此async/await并不是真正可行的,因此此代码改为使用ContinueWith().不过,它没有指定TaskScheduler,而

We are using this code snippet from StackOverflow to produce a Task that completes as soon as the first of a collection of tasks completes successfully. Due to the non-linear nature of its execution, async/await is not really viable, and so this code uses ContinueWith() instead. It doesn't specify a TaskScheduler, though, which a number of sources have mentioned can be dangerous because it uses TaskScheduler.Current when most developers usually expect TaskScheduler.Default behavior from continuations.

普遍的智慧似乎是,您应该始终将一个明确的TaskScheduler传递给ContinueWith.但是,对于何时使用其他TaskSchedulers最合适,我还没有明确的解释.

The prevailing wisdom appears to be that you should always pass an explicit TaskScheduler into ContinueWith. However, I haven't seen a clear explanation of when different TaskSchedulers would be most appropriate.

TaskScheduler.Default相反,最好将TaskScheduler.Current传递到ContinueWith()的情况的具体示例是什么?做出此决定时是否要遵循经验法则?

What is a specific example of a case where it would be best to pass TaskScheduler.Current into ContinueWith(), as opposed to TaskScheduler.Default? Are there rules of thumb to follow when making this decision?

对于上下文,这是我指的代码段:

For context, here's the code snippet I'm referring to:

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;
}

推荐答案

可能您需要选择一个适合于正在执行的委托实例执行的操作的任务计划程序.

Probably you need to choose a task scheduler that is appropriate for actions that an executing delegate instance performs.

请考虑以下示例:

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的示例,因为可以在线程池上安全地执行延续委托实例.

Your FirstSuccessfulTask() probably is the example where you can use TaskScheduler.Default, because the continuation delegate instance can be safely executed on a thread pool.

您还可以使用自定义任务计划程序在库中实现自定义计划逻辑.例如,请参阅奥尔良框架网站上的 Scheduler 页.

You can also use custom task scheduler to implement custom scheduling logic in your library. For example see Scheduler page on Orleans framework website.

有关更多信息,请检查:

For more information check:

  • It's All About the SynchronizationContext article by Stephen Cleary
  • TaskScheduler, threads and deadlocks article by Cosmin Lazar
  • StartNew is Dangerous article by Stephen Cleary

这篇关于什么时候应使用TaskScheduler.Current作为参数调用Task.ContinueWith?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆