2015-02-18 10 views
7

Date un'occhiata a questo codice:Come attendere fino Task.ContinueWith compito interiore finisce

private async Task InnerTask(bool outerTaskResult) 
    { 
     Console.WriteLine("2"); 
     await Task.Factory.StartNew(() => Thread.Sleep(10000)); 
     Console.WriteLine("3"); 
    } 

    private async void Button2_Click(object sender, RoutedEventArgs e) 
    { 
     var task = Task.FromResult(false); 
     Task<Task> aggregatedTask = task.ContinueWith(task1 => InnerTask(task1.Result)); 
     Console.WriteLine("1"); 
     await aggregatedTask; 
     Console.WriteLine("4"); 
    } 

L'output desiderato è:

1 
2 
3 
4 

ma ottengo:

1 
2 
4 
3 

Questo probabilmente ha qualcosa a che fare con InnerTask che viene eseguito su un thread diverso.

Sto usando ContinueWith perché le attività nel codice originale vengono create dinamicamente e accodate in questo modo.

L'utilizzo del metodo .Wait() (vedere di seguito) funziona, ma penso che sia una cattiva idea, poiché il metodo sta bloccando.

task.ContinueWith(task1 => InnerTask(task1.Result).Wait()) 

Qual è l'approccio corretto qui?

risposta

8

È possibile utilizzare TaskExtensions.Unwrap() (che è un metodo di estensione su Task<Task>) per aprire il compito outter e recuperare quella interna:

private async void Button2_Click(object sender, RoutedEventArgs e) 
{ 
    var task = Task.FromResult(false); 
    Task aggregatedTask = task.ContinueWith(task1 => InnerTask(task1.Result)).Unwrap(); 
    Console.WriteLine("1"); 
    await aggregatedTask; 
    Console.WriteLine("4"); 
} 

Si noti che per semplificare questa intera cosa, invece di ContinueWith continuazione stile si può await sui vostri compiti:

private async void Button2_Click(object sender, RoutedEventArgs e) 
{ 
    var task = Task.FromResult(false); 

    Console.WriteLine("1"); 

    var result = await task; 
    await InnerTask(result); 

    Console.WriteLine("4"); 
} 
+0

'Unwrap' non è effettivamente necessario. Il valore di ritorno di 'await aggregatedTask' nella domanda è in realtà il compito interno. –

+0

Ha un 'Compito ', è l'attività esterna su cui è interessante, ecco perché sta completando * prima * l'esterno 'Console.WriteLine'. Può semplicemente "attendere". –

+2

@PanagiotisKanavos Vero, non è * necessario *, si potrebbe anche "attendere" due volte. Entrambi farebbero la stessa cosa Questo è certamente un approccio appropriato. In C# 5.0 è possibile implementare 'Unwrap' come:' Public async Task Unwrap (questa attività task) {attendere l'attesa dell'attività; } '(La versione C# 4.0 è in realtà fastidiosamente lunga e noiosa.) – Servy

Problemi correlati