2014-09-05 3 views
5

ho questo codice di esempio:ContinueWith concatenamento non funziona come previsto

static void Main(string[] args) { 
     var t1 = Task.Run(async() => { 
      Console.WriteLine("Putting in fake processing 1."); 
      await Task.Delay(300); 
      Console.WriteLine("Fake processing finished 1. "); 
     }); 
     var t2 = t1.ContinueWith(async (c) => { 
      Console.WriteLine("Putting in fake processing 2."); 
      await Task.Delay(200); 
      Console.WriteLine("Fake processing finished 2."); 
     }); 
     var t3 = t2.ContinueWith(async (c) => { 
      Console.WriteLine("Putting in fake processing 3."); 
      await Task.Delay(100); 
      Console.WriteLine("Fake processing finished 3."); 
     }); 
     Console.ReadLine(); 
    } 

L'uscita della console mi sconcerta:

  • Mettere in lavorazione falso 1.
  • elaborazione falso finito 1.
  • Messa in elaborazione falsa 2.
  • Messa in elaborazione falsa 3.
  • elaborazione
  • falso finito 3.
  • elaborazione falso finito 2.

Sto cercando di catena dei compiti in modo da eseguire uno dopo l'altro, quello che sto facendo di sbagliato? E non posso usare attendere, questo è solo codice di esempio, in realtà sto facendo la coda ai compiti in arrivo (alcuni asincroni, altri no) e voglio eseguirli nello stesso ordine in cui sono entrati ma senza parallelismo, ContinueWith sembrava meglio della creazione un ConcurrentQueue e la gestione di tutto me stesso, ma semplicemente non funziona ...

+0

l'attestazione di attesa "divide" l'attività. Quindi t1 fa riferimento solo alla prima metà. Sono sorpreso Mettendo in elaborazione falsa 2 non accade immediatamente dopo Mettendo in elaborazione falsa 1 –

+0

@WeylandYutani Spiego nella mia risposta perché quello non accade. – Servy

+0

in realtà sto parlando di spazzatura di nuovo, quindi ignorami –

risposta

8

Dai un'occhiata al tipo di t2. È un Task<Task>. t2 sarà completato quando termina l'avvio dell'attività che esegue il lavoro effettivo non al termine di tale lavoro.

La modifica più piccola al codice per farlo funzionare sarebbe aggiungere unwrap dopo la seconda e la terza chiamata a ContinueWith, in modo da ottenere l'attività che rappresenta il completamento del lavoro.

La soluzione più idiomatica sarebbe semplicemente rimuovere le chiamate ContinueWith interamente e utilizzare semplicemente await per aggiungere continuazioni alle attività.

È interessante notare che, si dovrebbe vedere lo stesso comportamento per t1 se è stato utilizzato Task.Factory.StartNew, ma Task.Run è specificamente progettato per funzionare con async lambda e in realtà scarta internamente tutti Action<Task> delegati per restituire il risultato del compito restituito, piuttosto che un compito che rappresenta l'avvio di tale attività, motivo per cui non è necessario scartare questa attività.

+0

Grazie, Unwrap funziona (non è possibile utilizzare Attendere poiché il codice reale non è un elenco lineare, sto ricevendo un flusso di attività da eseguire in serie, quindi devono essere accodati. Ovviamente mi piacerebbe invece utilizzare Attendere invece:)). –

+0

@MartinSykora Sì, si * potrebbe * usare 'await' invece di' ContinueWith', anche se è difficile mostrarti come senza conoscere le specifiche della tua situazione. Molto probabilmente sarai semplicemente in grado di scrivere un metodo 'async' che accetta un compito, lo attende, insieme ad aspettare altre cose e chiamare altri metodi, e quindi restituire un' Task'. Praticamente ogni volta che chiami 'ContinueWith' su un'attività che probabilmente puoi chiamare' await' su di esso e lo faccia uscire più bello, con solo rare eccezioni. – Servy

2

in realtà sono Queuing compiti in arrivo (alcuni asincrono, alcuni non) e si vuole eseguirli nello stesso ordine sono venuti in, ma senza alcun parallelismo

probabilmente si desidera utilizzare TPL Dataflow per quella. Nello specifico, ActionBlock.

var block = new ActionBlock<object>(async item => 
{ 
    // Handle synchronous item 
    var action = item as Action; 
    if (action != null) 
    action(); 

    // Handle asynchronous item 
    var func = item as Func<Task>; 
    if (func != null) 
    await func(); 
}); 

// To queue a synchronous item 
Action synchronous =() => Thread.Sleep(1000); 
block.Post(synchronous); 

// To queue an asynchronous item 
Func<Task> asynchronous = async() => { await Task.Delay(1000); }; 
blockPost(asynchronous); 
+0

Bello, potrebbe essere proprio quello di cui ho bisogno se funziona, sicuramente sarebbe più elegante :-) –