L'avvio potrebbe non essere richiamato per un'attività in stile promessa. l'eccezione sta arrivando


108

Sto creando una semplice applicazione desktop wpf. L'interfaccia utente ha solo un pulsante e un codice in un file .cs come.

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

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

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

L'avvio potrebbe non essere richiamato per un'attività in stile promessa.

Qualcuno può aiutare perché è così?

Risposte:


171

Stai ricevendo quell'errore perché la Taskclasse ha già avviato l'attività prima di dartela. Dovresti sempre e solo richiamare Startun'attività che crei chiamando il suo costruttore, e non dovresti nemmeno farlo a meno che tu non abbia una ragione valida per non avviare l'attività quando la crei; se vuoi che inizi subito dovresti usare Task.Runo Task.Factory.StartNewsia per creare che per iniziarne uno nuovo Task.

Quindi, ora sappiamo che dobbiamo sbarazzarci di quel fastidioso Start. Eseguirai il tuo codice e scoprirai che la finestra del messaggio viene visualizzata subito, non 5 secondi dopo, che succede?

Bene, Task.Delayti dà solo un'attività che sarà completata in 5 secondi. Non interrompe l'esecuzione del thread per 5 secondi. Quello che vuoi fare è avere del codice che viene eseguito al termine di quell'attività. Ecco a cosa ContinueWithserve. Ti consente di eseguire del codice al termine di una determinata attività:

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

Ciò si comporterà come previsto.

Potremmo anche sfruttare la awaitparola chiave di C # 5.0 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 va oltre lo scopo di questa domanda, 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] subito in entrambi i casi. Detto questo, awaitè molto potente e ci permette di scrivere metodi che sembrano semplici e diretti, ma che sarebbero molto più difficili e complicati da scrivere usando ContinueWithdirettamente. Inoltre semplifica notevolmente la gestione degli errori, eliminando molto 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 ha detto Servy, l'attività è già iniziata, quindi non ti resta che attendere (.Wait ()):

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

1
La chiamata Wait()a un'attività bloccherà il thread corrente finché l'attività non verrà risolta. Non è quasi mai quello che vuoi che accada.
Jeremy

1
@ Jeremy: In effetti, vale la pena prestare attenzione al comportamento che hai citato, ma in questo caso la sua FunzioneA stava già bloccando il thread corrente, quindi ho pensato che stesse solo cercando un modo per determinare quando l'attività è stata completata. Per chiarire la differenza tra Wait e async (per i futuri lettori) si prega di leggere questo link
Sergiu
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.