前一段时间我曾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>
答案 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);
}