2012-11-26 18 views
5

Voglio catena Task s, quindi avviare la catena in parallelo. Questo frammento è solo per illustrare la mia domanda:Modo corretto per concatenare Task

 var taskOrig = new Task(() => { }); 
     var task = taskOrig; 
     foreach (var msg in messages) 
     { 
      task=task.ContinueWith(t => Console.WriteLine(msg)); 
     } 
     taskOrig.Start(); 

Tutto funziona bene, tranne un po 'perfezionista dentro di me non piace aver metodo vuoto eseguito prima () => { }.

C'è un modo per evitarlo?

Capisco Ha a malapena influire sulle prestazioni (a meno che non lo facciate molto spesso), ma ancora. Le prestazioni sono importanti nel mio caso, quindi controllare se l'attività esiste in ogni iterazione non è il modo di farlo.

+1

"la performance è nel mio caso, in modo da controllare se compito esiste in ogni iterazione non è il modo per farlo. ": il tempo necessario è trascurabile rispetto all'esecuzione effettiva del compito. A meno che tu non abbia realmente misurato un successo in termini di prestazioni, è chiaramente un caso di ottimizzazione prematura. –

+0

@ThomasLevesque hai probabilmente ragione, ho solo pensato che forse mi sono perso qualcosa nell'API di creazione 'Task'. Avrò la possibilità di misurare il successo delle prestazioni più tardi. – Anri

+0

Potreste trovare interessante TPL DataFlow – sll

risposta

3

Si potrebbe A fare questo:

Task task = Task.FromResult<object>(null); 
foreach (var msg in messages) 
{ 
    task = task.ContinueWith(t => Console.WriteLine(msg)); 
} 

La soluzione precedente non funzionerà in 4.0. In 4.0 avresti bisogno di fare quanto segue invece: (. Puoi spostare SetResult a prima del ciclo foreach, se si preferisce)

var tcs = new TaskCompletionSource<object>(); 
Task task = tcs.Task; 
foreach (var msg in messages) 
{ 
    task = task.ContinueWith(t => Console.WriteLine(msg)); 
} 

tcs.SetResult(null); 

Tecnicamente non è lo stesso che le continuazioni inizierà l'esecuzione, mentre stai ancora aggiungendo di più. È improbabile che questo sia un problema.

Un'altra opzione sarebbe quella di utilizzare qualcosa di simile:

public static Task ForEachAsync<T>(IEnumerable<T> items, Action<T> action) 
{ 
    return Task.Factory.StartNew(() => 
    { 
     foreach (T item in items) 
     { 
      action(item); 
     } 
    }); 
} 

Un esempio d'uso potrebbe essere:

ForEachAsync(messages, msg => Console.WriteLine(msg)); 
+0

Una nota: 'Task.FromResult()' è nuovo in .Net 4.5, in .Net 4.0, dovresti farlo manualmente usando 'TaskCompletionSource'. – svick

+0

@svick Right, soluzione aggiunta. – Servy

+0

Nice, grazie, 'Task.FromResult (null)' - questo è esattamente ciò che mi mancava nel Task Parallel lib – Anri

2

Un modo per farlo, è quello di creare un'attività in loop se è nullo, ma il codice che fornisci sembra meglio per me:

Task task = null; 
foreach (var msg in messages) 
{ 
    if (task == null) 
     task = new Task(() => Console.WriteLine(msg)) 
    else 
     task = task.ContinueWith(t => Console.WriteLine(msg)); 
} 
task.Start(); 
+0

Tecnicamente - sì, ma significa che dovrò controllarlo ogni ciclo in modo che sia ancora più lento. Le prestazioni contano, dovrei aggiungere questo alla domanda – Anri

+0

codice che fornisci è davvero bello, ma aspettiamo altre opzioni –

1

Forse questo:

if(messages.Length > 0) 
{ 
    Task task = new Task(t => Console.WriteLine(messages[0])); 

    for(int i = 1; i < messages.Length; i++) 
    { 
     task = task.ContinueWith(t => Console.WriteLine(messages[i])); 
    } 
    task.Start(); 
} 
+0

È l'opzione più efficiente, ma è meno leggibile ... –

+0

Grazie, è un'opzione, ma non funzionerà sempre. a volte tutto ciò che hai è un enumeratore, e le attività possono anche essere aggiunte in altri modi – Anri

+0

@Anri Puoi fare la stessa cosa anche con l'enumeratore (accedendo manualmente ai suoi 'MoveNext()' e 'Current'), eccetto che sarà ancora di più codice e ancora più disordinato. – svick

Problemi correlati