按完成时间排序任务,同时在参数列表中跟踪它们的索引?
Ordering Tasks by completion time while keeping track of their index in an argument list?
我最近 关于清理一些代码的可能性,代码本来是要等待 List<Task<T>>
中的每个 Task
完成但取消所有 Task
如果 return 编辑了一些错误的值。
一个名为 Servy 的用户雄辩地按完成时间订购了 List<Task<T>>
。在稍微阅读了答案后,我 think/thought 我 understand/stood 方法。然后我去使用这个方法,但很快意识到一个问题。我需要能够在 Task
完成时识别它们。但是 Servy 建议的 Order
方法无法做到这一点(因为我无法将 Order
编辑的任务延续 return 与我最初提供的 List<Task<>>
进行比较。
所以我将方法更改为 return a Tuple<T, int>
其中 int
表示提供的参数中 Task
的原始索引。
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;
我面临的问题是 Tuple
returned 中的索引似乎总是等于传递给 List<Task<>>
的原始 Count
=25=] 不管任务的顺序如何 returned。
我假设由于这个问题,我一开始并不完全理解这个方法是如何工作的,尽管它似乎应该工作得很好。
谁能解释一下问题并提供解决方案?
发生这种情况是因为您在 Action<>
.
中使用了 i
变量
但是这个动作的代码在你创建Action
的时候并没有执行,但是当任务完成的时候,变量i
的值是taskList.Count
(当for
循环结束)。
您只需在 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);
}
您可以阅读这篇文章question/answers了解更多详情。
我最近 List<Task<T>>
中的每个 Task
完成但取消所有 Task
如果 return 编辑了一些错误的值。
一个名为 Servy List<Task<T>>
。在稍微阅读了答案后,我 think/thought 我 understand/stood 方法。然后我去使用这个方法,但很快意识到一个问题。我需要能够在 Task
完成时识别它们。但是 Servy 建议的 Order
方法无法做到这一点(因为我无法将 Order
编辑的任务延续 return 与我最初提供的 List<Task<>>
进行比较。
所以我将方法更改为 return a Tuple<T, int>
其中 int
表示提供的参数中 Task
的原始索引。
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;
我面临的问题是 Tuple
returned 中的索引似乎总是等于传递给 List<Task<>>
的原始 Count
=25=] 不管任务的顺序如何 returned。
我假设由于这个问题,我一开始并不完全理解这个方法是如何工作的,尽管它似乎应该工作得很好。
谁能解释一下问题并提供解决方案?
发生这种情况是因为您在 Action<>
.
i
变量
但是这个动作的代码在你创建Action
的时候并没有执行,但是当任务完成的时候,变量i
的值是taskList.Count
(当for
循环结束)。
您只需在 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);
}
您可以阅读这篇文章question/answers了解更多详情。