2016-02-06 13 views
5

Nel suo corso Pluralsight Asynchronous C# 5, Jon Skeet prevede questa implementazione di un metodo di estensione convenienza chiamato InCOmpletionOrder:C'è un motivo per preferire una di queste implementazioni sopra l'altro

public static IEnumerable<Task<T>> InCompletionOrder<T>(this IEnumerable<Task<T>> source) 
{ 
    var inputs = source.ToList(); 
    var boxes = inputs.Select(x => new TaskCompletionSource<T>()).ToList(); 
    int currentIndex = -1; 

    foreach (var task in inputs) 
    { 
     task.ContinueWith(completed => 
     { 
      var nextBox = boxes[Interlocked.Increment(ref currentIndex)]; 
      PropagateResult(completed, nextBox); 
     }, TaskContinuationOptions.ExecuteSynchronously); 
    } 

    return boxes.Select(box => box.Task); 
} 

private static void PropagateResult<T>(Task<T> completedTask, 
     TaskCompletionSource<T> completionSource) 
{ 
    switch(completedTask.Status) 
    { 
     case TaskStatus.Canceled: 
      completionSource.TrySetCanceled(); 
      break; 
     case TaskStatus.Faulted: 
      completionSource.TrySetException(completedTask.Exception.InnerExceptions); 
      break; 
     case TaskStatus.RanToCompletion: 
      completionSource.TrySetResult(completedTask.Result); 
      break; 
     default: 
      throw new ArgumentException ("Task was not completed."); 
    } 
} 

In this question, Martin Neal fornisce un, apparentemente più elegante, implementazione utilizzando ritorno resa

public static IEnumerable<Task<T>> InCompletionOrder<T>(this IEnumerable<Task<T>> source) 
{ 
    var tasks = source.ToList(); 

    while (tasks.Any()) 
    { 
     var t = Task.WhenAny(tasks); 
     yield return t.Result; 
     tasks.Remove(t.Result); 
    } 
} 

Essere ancora un po 'nuova per l'impianto di perforazione il nostro di programmazione asincrona, chiunque può descrivere le preoccupazioni specifiche che potrebbero sorgere con l'implementazione di Martin Neal che sono correttamente risolte dall'implementazione più coinvolta di Jon Skeet

+0

@MickyD: Esatto; ma esita a quadruplicare il volume del codice fino a quando non capisco, almeno a grandi linee, perché. ** usr ** ha, di seguito, fornito a me due ottimi motivi per farlo. –

+0

concordato. Ho appena avuto un problema con "apparentemente più elegante". Non si può sempre equiparare il conteggio della linea con l'eleganza per tutti gli aspetti della programmazione. Servy rende alcuni punti positivi di seguito. :) – MickyD

risposta

5

La seconda soluzione ha un problema di complessità quadratica del tempo. Il ciclo esegue N volte e ogni chiamata WhenAny aggiunge N continuazioni a tali attività. Non usare quel codice tranne se si è sicuri che il numero di attività è molto piccolo.

La chiamata Remove causa anche una complessità temporale quadratica.

Inoltre, il secondo pezzo di codice sta bloccando. Recuperai le attività appena completate. InCompletionOrder ti dà queste attività immediatamente e si completano più tardi.

Considerare InCompletionOrder come metodo di libreria. Inseriscilo in un file di utilità e non causerà problemi di manutenzione. Il suo comportamento non potrà mai cambiare. Non vedo la dimensione del codice come un problema qui.

+2

Jon's gestisce anche le attività escluse e cancellate correttamente, mentre l'altro frammento funziona fondamentalmente solo se tutte le attività vengono completate correttamente. Questo, e il fatto che stia bloccando (fondamentalmente rendendo il tipo di ritorno di 'IEnumerable >' essenzialmente una bugia) sono * di gran lunga * più importanti dei problemi di prestazioni, quindi avrei guidato con quei due punti e menzionato solo le prestazioni come un punto minore. – Servy

+0

@Servy sei sicuro del comportamento dell'eccezione? Il secondo frammento non accede al risultato delle attività che restituisce. L'altro punto è ben noto. – usr