C# 按完成时间排序任务,同时在参数列表中跟踪其索引?
我最近讨论了清理某些代码的可能性,该代码意味着等待C# 按完成时间排序任务,同时在参数列表中跟踪其索引?,c#,task-parallel-library,ienumerable,C#,Task Parallel Library,Ienumerable,我最近讨论了清理某些代码的可能性,该代码意味着等待列表中的每个任务完成,但如果返回了一些错误值,则取消所有任务 一个名为Servy的用户,它雄辩地按完成时间排序一个列表。把答案读了一遍后,我想我理解了这个方法。然后我开始使用这种方法,但很快就意识到了一个问题。我需要能够在任务完成时识别任务。但是Servy建议的Order方法无法实现这一点(因为我无法将Order返回的任务延续与我最初提供的列表进行比较) 因此我改变了方法,返回一个Tuple,其中int表示所提供参数中任务的原始索引 public
列表中的每个任务
完成,但如果返回了一些错误值,则取消所有任务
一个名为Servy的用户,它雄辩地按完成时间排序一个列表。把答案读了一遍后,我想我理解了这个方法。然后我开始使用这种方法,但很快就意识到了一个问题。我需要能够在任务完成时识别任务。但是Servy建议的Order
方法无法实现这一点(因为我无法将Order
返回的任务延续与我最初提供的列表进行比较)
因此我改变了方法,返回一个Tuple
,其中int
表示所提供参数中任务的原始索引
public static IEnumerable<Task<Tuple<T, int>>> OrderByCompletion<T>(IEnumerable<Task<T>> tasks)
{
var taskList = tasks.ToList();
var taskSources = new BlockingCollection<TaskCompletionSource<Tuple<T, int>>>();
var taskSourceList = new List<TaskCompletionSource<Tuple<T, int>>>(taskList.Count);
for (int i = 0; i < taskList.Count; i++)
{
var task = taskList[i];
var newSource = new TaskCompletionSource<Tuple<T, int>>();
taskSources.Add(newSource);
taskSourceList.Add(newSource);
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(new Tuple<T, int>(t.Result, i));
}, CancellationToken.None, TaskContinuationOptions.PreferFairness, TaskScheduler.Default);
}
return taskSourceList.Select(tcs => tcs.Task);
}
// Usage
foreach(var task in myTaskList.OrderByCompletion())
Tuple<Boolean, int> result = await task;
公共静态IEnumerable OrderByCompletion(IEnumerable任务)
{
var taskList=tasks.ToList();
var taskSources=new BlockingCollection();
var taskSourceList=新列表(taskList.Count);
for(int i=0;i
{
var source=taskSources.Take();
如果(t.IsCanceled)
source.TrySetCanceled();
否则,如果(t.IsFaulted)
source.TrySetException(t.Exception.InnerExceptions);
否则,如果(t.已完成)
TrySetResult(新元组(t.Result,i));
},CancellationToken.None,TaskContinuationOptions.PreferFairity,TaskScheduler.Default);
}
返回taskSourceList.Select(tcs=>tcs.Task);
}
//用法
foreach(myTaskList.OrderByCompletion()中的var任务)
元组结果=等待任务;
我面临的问题是,返回的元组中的索引似乎总是等于传递给OrderByCompletion
的原始列表的Count
,而不管返回任务的顺序如何
我认为,由于这个问题,我并不完全理解这个方法最初是如何工作的,尽管它似乎应该工作得很好
有人能解释问题并提供解决方案吗?发生这种情况是因为您在操作中使用了i
变量
但是,在创建操作时不会执行此操作的代码,而是在任务完成时,变量i
具有值taskList.Count
(当for
循环完成时)
您只需在
的中添加一个额外变量即可解决问题:
for (int i = 0; i < taskList.Count; i++)
{
var task = taskList[i];
var newSource = new TaskCompletionSource<Tuple<T, int>>();
taskSources.Add(newSource);
taskSourceList.Add(newSource);
int index = i; // <- add this variable.
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(new Tuple<T, int>(t.Result, index));
}, CancellationToken.None, TaskContinuationOptions.PreferFairness, TaskScheduler.Default);
}
for(int i=0;i
有关更多详细信息,请阅读本节。发生这种情况是因为您在操作中使用了i
变量
但是,在创建操作时不会执行此操作的代码,而是在任务完成时,变量i
具有值taskList.Count
(当for
循环完成时)
您只需在
的中添加一个额外变量即可解决问题:
for (int i = 0; i < taskList.Count; i++)
{
var task = taskList[i];
var newSource = new TaskCompletionSource<Tuple<T, int>>();
taskSources.Add(newSource);
taskSourceList.Add(newSource);
int index = i; // <- add this variable.
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(new Tuple<T, int>(t.Result, index));
}, CancellationToken.None, TaskContinuationOptions.PreferFairness, TaskScheduler.Default);
}
for(int i=0;i
您可以阅读此文了解更多详细信息。Ohhhhhhh耶!我以前在一篇博客文章中看到过这个“问题”(我想是Eric Lippert?)。这完全有道理。非常感谢。哦,是的!我以前在一篇博客文章中看到过这个“问题”(我想是Eric Lippert?)。这完全有道理。非常感谢。