Si consiglia di utilizzare prevTask.Wait () con ContinueWith (dalla libreria Tasks)?


88

Così recentemente mi è stato detto che il modo in cui stavo usando il mio .ContinueWith for Tasks non era il modo corretto di usarli. Devo ancora trovare prove di ciò su Internet, quindi vi chiederò e vedremo qual è la risposta. Ecco un esempio di come utilizzo .ContinueWith:

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });
}

Ora so che questo è un semplice esempio e verrà eseguito molto velocemente, ma supponiamo che ogni attività esegua un'operazione più lunga. Quindi, quello che mi è stato detto è che in .ContinueWith, devi dire prevTask.Wait (); altrimenti potresti lavorare prima del termine dell'attività precedente. È anche possibile? Ho pensato che la mia seconda e terza attività sarebbero state eseguite solo al termine dell'attività precedente.

Quello che mi è stato detto come scrivere il codice:

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 3");
    });
}

Risposte:


115

Ehhh .... credo che ad alcune delle risposte attuali manchi qualcosa: cosa succede con le eccezioni?

L'unico motivo per cui chiameresti Waituna continuazione sarebbe osservare una potenziale eccezione dall'antecedente nella continuazione stessa. La stessa osservazione accadrebbe se si accedesse Resultnel caso di a Task<T>e anche se si accedesse manualmente alla Exceptionproprietà. Francamente, non chiamerei Waito non accedo Resultperché se c'è un'eccezione pagherai il prezzo di rialzarla che è un sovraccarico non necessario. Invece puoi semplicemente controllare la IsFaultedproprietà dall'antecedente Task. In alternativa è possibile creare flussi di lavoro biforcuti concatenando più continuazioni di pari livello che si attivano solo in base al successo o all'insuccesso con TaskContinuationOptions.OnlyOnRanToCompletione TaskContinuationOptions.OnlyOnFaulted.

Ora, non è necessario osservare l'eccezione dell'antecedente nella continuazione, ma potresti non volere che il tuo flusso di lavoro vada avanti se, ad esempio, il "Passaggio 1" non è riuscito. In tal caso: specificare TaskContinuationOptions.NotOnFaultedle ContinueWithchiamate impedirebbe alla logica di continuazione di attivarsi.

Tieni presente che, se le tue continuazioni non osservano l'eccezione, la persona che attende il completamento di questo flusso di lavoro complessivo sarà quella che lo osserverà. O stanno andando Waita Taskmonte o hanno virato sulla propria continuazione per sapere quando sarà completa. In quest'ultimo caso, la loro continuazione richiederebbe l'utilizzo della suddetta logica di osservazione.


2
Finalmente qualcuno dia una risposta corretta. @ Travyguy9 Per favore leggi questa risposta a @DrewMarsh e leggi di più suTaskContinuationOptions
Jasper

2
Ottima risposta, stavo cercando "Tieni presente che, se le tue continuazioni non osservano l'eccezione, la persona che attende il completamento di questo flusso di lavoro complessivo sarà quella che lo osserverà". Una domanda però, quando il tuo compito non è in attesa, chi è il cameriere predefinito? (Impossibile trovare la risposta a questo)
Thibault D.

20

Lo stai usando correttamente.

Crea una continuazione che viene eseguita in modo asincrono quando l'attività di destinazione viene completata.

Fonte: metodo Task.ContinueWith (azione come MSDN)

Dover chiamare prevTask.Wait()in ogni Task.ContinueWithinvocazione sembra un modo strano per ripetere la logica non necessaria, cioè fare qualcosa per essere "super sicuro" perché in realtà non capisci cosa fa un certo pezzo di codice. Come controllare un valore nullo solo per lanciare un fileArgumentNullException dove sarebbe stato comunque lanciato.

Quindi no, chi ti ha detto che è sbagliato e probabilmente non capisce perché Task.ContinueWithesiste.


16

Chi vi ha detto che?

Citando MSDN :

Crea una continuazione che viene eseguita in modo asincrono quando l'attività di destinazione viene completata.

Inoltre, quale sarebbe lo scopo di Continua con se non fosse in attesa del completamento dell'attività precedente?

Puoi anche testarlo da solo:

Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
        Thread.Sleep(2000);
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("I waited step 1 to be completed!");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });

5

Da MSDN in poiTask.Continuewith

L'attività restituita non verrà pianificata per l'esecuzione fino al completamento dell'attività corrente. Se i criteri specificati tramite il parametro continuationOptions non vengono soddisfatti, l'attività di continuazione verrà annullata anziché pianificata.

Penso che il modo in cui ti aspetti che funzioni nel primo esempio sia il modo corretto.



0

Accedendo Task.Resultstai effettivamente facendo una logica simile atask.wait


Sì. Possiamo evitare il metodo Wait (). Ma funziona solo con le attività risultato, ad es. Attività <bool>
Alexander Ulmaskulov

Downvoted perché non risponde alla domanda reale. Aggiunge un certo valore, ma dovrebbe essere un commento.
Sinaesthetic

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.