完成订购任务后获取对原始任务的引用?

时间:2016-10-13 20:37:15

标签: c# exception-handling task-parallel-library task

前一段时间我曾a question询问了一个方法,该方法通过完成对List<Task<T>>>进行排序,并返回表示原始int的索引的Task List<Task<T>>给出。

我知道我可能不需要返回此int来确定哪个特定Task已完成,并且我可以查询返回的Task以获取此信息。

作为旁注,我已经改变了方法以订购List<Task>。我最初使用了Task<T>,它返回bool来表示Task<T>是否成功完成了工作。我现在简单地抛出一个Exception的子类,它提供了有关Task失败的方式和原因的更多信息。

我对此问题的想法来自这样一个问题:当此方法的Task<int>引发Exception时,我无法确定哪个特定Task扔了Exception因为我无法查询Task<int>.Result的{​​{1}}原始索引。

再次,如果我可以查询从原始列表引用的Task返回的Task<int>(现在只是Task),我可以简单地比较引用。

以下是现在存在的方法(对于此方法的original code,可归功于Servy。也可能来自Jon Skeet的这个blog post

Task

我的第一个问题是,此方法使用public static IEnumerable<Task<int>> OrderByCompletion(IEnumerable<Task> tasks) { var taskList = tasks.ToList(); var taskSources = new BlockingCollection<TaskCompletionSource<int>>(); var taskSourceList = new List<TaskCompletionSource<int>>(taskList.Count); for (int i = 0; i < taskList.Count; i++) { var task = taskList[i]; var newSource = new TaskCompletionSource<int>(); taskSources.Add(newSource); taskSourceList.Add(newSource); int index = i; task.ContinueWith(t => { var source = taskSources.Take(); if (t.IsCanceled) source.TrySetCanceled(); else if (t.IsFaulted) source.TrySetException(t.Exception.InnerExceptions); else if (t.IsCompleted) source.TrySetResult(index); }, CancellationToken.None, TaskContinuationOptions.PreferFairness, TaskScheduler.Default); } return taskSourceList.Select(tcs => tcs.Task); } 来跟踪参数列表中TaskCompletionSource<T>的{​​{1}}。看到TResult现在没有返回任何值并使用Task这是没用的,虽然它的使用是不可避免的,因为没有非通用参数化List<Task>> 但这不是真的问题,因为我可以返回一些垃圾值。

那么,对于问题本身,我是否可以询问Exception以获取对其跟踪的TaskCompletionSource<T>的引用?

据我所知,Task<(unused return value)>没有关于它正在跟踪的Task的信息。它只是“看起来像”原始的TaskCompletionSource<T>

子类Task的唯一选项是添加一个引用它跟踪的Task的属性,在方法中设置属性的值,然后询问该属性以获取引用吗? / p>

TaskCompletionSource<T>

1 个答案:

答案 0 :(得分:3)

只需看看WhenAny解决此类问题的方法。让方法返回Task,其结果是Task,它在逻辑上表示完成,而不是计算与该任务相同的逻辑值。如果他们只关心任务的结果(或错误状态),这会强制调用者解开该任务,但如果您不想总是想要解包,那么您可以返回整个Task 。它实际上简化了代码,没有Order方法为你解开任务。

public static IEnumerable<Task<Task>> Order2(this IEnumerable<Task> tasks)
{
    var taskList = tasks.ToList();

    var taskSources = new BlockingCollection<TaskCompletionSource<Task>>();

    var taskSourceList = new List<TaskCompletionSource<Task>>(taskList.Count);
    foreach (var task in taskList)
    {
        var newSource = new TaskCompletionSource<Task>();
        taskSources.Add(newSource);
        taskSourceList.Add(newSource);

        task.ContinueWith(t => taskSources.Take().TrySetResult(t),
            CancellationToken.None, TaskContinuationOptions.PreferFairness, TaskScheduler.Default);
    }

    return taskSourceList.Select(tcs => tcs.Task);
}