代码之家  ›  专栏  ›  技术社区  ›  KDecker

按完成时间排序任务,同时在参数列表中跟踪其索引?

  •  0
  • KDecker  · 技术社区  · 8 年前

    我最近 asked a question 关于清理一些代码的可能性,该代码旨在等待每一个 Task 在一个 List<Task<T>> 完成但取消全部 任务 如果返回了错误的值。

    名为Servy的用户 quickly produced a method 它雄辩地命令 列表<任务<T>> 在其完成时间之前。在仔细阅读了一下答案后,我想我理解了这个方法。然后我开始使用这个方法,但很快就意识到了一个问题。我需要能够识别 任务 s,因为他们正在完成。但是 Order Servy建议的方法没有提供这样的方法(因为我无法比较 顺序 到我的 List<Task<>> 我最初提供的)。

    所以我改变了方法,返回 Tuple<T, int> 其中 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;
    

    我面临的问题是 Tuple 返回的似乎总是等于 Count 原件的 列表<任务<>> 传递给 OrderByCompletion 无论任务返回的顺序如何。

    我认为,由于这个问题,我不完全理解这个方法的工作原理,尽管它看起来应该很好。

    有人能解释这个问题并提出解决方案吗?

    1 回复  |  直到 7 年前
        1
  •  2
  •   Community Egal    7 年前

    发生这种情况是因为您正在使用 i 变量 Action<> .

    但当您创建 Action ,但当任务完成时 具有价值 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);
    }
    

    你可以读这个 question/answers 了解更多详细信息。