2013-01-09 3 views
81

Sto creando una semplice applicazione desktop wpf. UI ha solo un pulsante e il codice in file .cs come.L'avvio non può essere chiamato su un'attività di tipo promessa. sta arrivando l'eccezione

private void Button_Click_2(object sender, RoutedEventArgs e) 
{ 
    FunctionA(); 
} 

public void FunctionA() 
{ 
    Task.Delay(5000).Start(); 
    MessageBox.Show("Waiting Complete"); 
} 

Ma sorprendentemente linea sta gettando Task.Delay(5000).Start(); un InvalidOperationException:

di inizio non può essere chiamato su un compito promessa stile.

Qualcuno può aiutare perché è così?

risposta

120

Hai trovato questo errore perché la classe Task già iniziato il compito prima di dare a voi. Dovresti sempre chiamare lo Start su un'attività che hai creato chiamando il suo costruttore, e non dovresti nemmeno farlo se non hai un valido motivo per non avviare l'attività quando la crei; se vuoi iniziare subito, devi utilizzare Task.Run o Task.Factory.StartNew per creare e avviare un nuovo Task.

Quindi, ora sappiamo di sbarazzarci di quel fastidioso Start. Esegui il tuo codice e scopri che la finestra di messaggio viene mostrata subito, non 5 secondi dopo, che succede?

Bene, Task.Delay ti dà solo un compito che sarà completato in 5 secondi. Non ferma l'esecuzione del thread per 5 secondi. Quello che vuoi fare è avere un codice che viene eseguito al termine di quell'attività. Questo è ciò che è ContinueWith. Ti consente di eseguire codice dopo che è stata eseguita una determinata attività:

public void FunctionA() 
{ 
    Task.Delay(5000) 
    .ContinueWith(t => 
    { 
     MessageBox.Show("Waiting Complete"); 
    }); 
} 

Questo si comporterà come previsto.

Potremmo anche sfruttare C# 5.0 di await parola chiave per aggiungere continuazioni più facilmente:

public async Task FunctionA() 
{ 
    await Task.Delay(5000); 
    MessageBox.Show("Waiting Complete"); 
} 

Mentre una spiegazione completa di quello che sta succedendo qui è oltre la portata di questo problema, il risultato finale è un metodo che si comporta in modo molto simile al metodo precedente; mostrerà una finestra di messaggio 5 secondi dopo aver chiamato il metodo, ma il metodo stesso tornerà [quasi] immediatamente in entrambi i casi. Detto questo, await è molto potente e ci consente di scrivere metodi che sembrano semplici e diretti, ma sarebbe molto più difficile e complicato scrivere usando direttamente ContinueWith. Inoltre semplifica enormemente la gestione degli errori, estrapolando un sacco di codice standard.

1

Prova questo.

private void Button_Click_2(object sender, RoutedEventArgs e) 
{ 
    FunctionA(); 
} 

public async void FunctionA() 
{ 
    await Task.Delay(5000); 
    MessageBox.Show("Waiting Complete"); 
} 
-4

Come Servy detto, il compito è già iniziato, quindi tutto quello che resta da fare è aspettare per esso (.Wait()):

private void Button_Click_2(object sender, RoutedEventArgs e) 
{ 
    FunctionA(); 
} 
public void FunctionA() 
{ 
    Task.Delay(5000).Wait(); 
    MessageBox.Show("Waiting Complete"); 
} 
+0

chiamata 'wait()' su un compito blocca la thread corrente fino a quando l'attività non si risolve. Non è quasi mai quello che vuoi succedere. – Jeremy

+0

@Jeremy: In effetti, vale la pena prestare attenzione al comportamento che hai menzionato, ma in questo caso la sua FunctionA stava già bloccando il thread corrente, quindi ho pensato che stia solo cercando un modo per determinare quando l'attività è stata completata. Per chiarire la differenza tra Wait e async (per i futuri lettori) leggi questo [collegamento] (http://stackoverflow.com/questions/9519414/whats-the-difference- between-task-start-wait-and-async- attendere) – Sergiu