2016-04-22 7 views
0

私は最近asked a questionいくつかのコードをクリーンアップする可能性について、の各Taskが完了するのを待っていましたが、すべて偽の値を返した場合はすべてTaskを取り消すことを意味していました。引数リストのインデックスを追跡しながら完了時間別にタスクを注文しますか?

Servy quickly produced a methodという名前のユーザーが、納得のいく返答をしてList<Task<T>>を注文します。答えを少し読んだ後、私は思っています/私は理解していると思った/方法を立っていた。私はその後、この方法を使用するようになりましたが、すぐに問題を認識しました。彼らが完了したときに私はTaskを認識できる必要があります。しかし、Orderメソッドの提案では、これを行う手段がありません。Orderによって返されたタスクの継続と、私がList<Task<>>を元々供給したものと比較することはできません。

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の内部インデックスは常にOrderByCompletionへのタスクの順序が返さ関係なく渡された元List<Task<>>Countに等しくなるように思われることです。

私は、この問題の結果、最初にこの方法がどのように機能したかを完全に理解していないと思いますが、うまくいくはずです。

誰でも問題を説明して解決策を提示できますか?

答えて

2

これはAction<>の内部でi変数を使用しているために発生します。

しかし、このアクションのコードは、あなたのActionを作成するときに実行されていませんが、タスクが完了すると、その変数iは(forループが終了した)値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を読むことができます。

+0

オハイオ州ええ!私はブログの記事の前にこの "問題"を見てきました(私はEric Lippertによって考えていますか?)。これは全く意味をなさない。ありがとうございました! – KDecker

関連する問題