2013-01-09 13 views
81

Tworzę prostą aplikację na komputer typu wpf. Interfejs użytkownika ma tylko przycisk i kod w pliku .cs jak.Start może nie zostać wywołany w zadaniu stylu obietnicy. wyjątek nadchodzi

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

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

Ale zaskakująco linia Task.Delay(5000).Start(); jest rzucanie InvalidOperationException:

Start nie można nazwać na zadaniu obietnica stylu.

Czy ktoś może pomóc, dlaczego tak jest?

Odpowiedz

120

Otrzymujesz ten błąd, ponieważ klasa Task już rozpoczęła zadanie przed podaniem go do ciebie. Powinieneś zawsze wywoływać Start dla zadania, które tworzysz, wywołując jego konstruktor, a nawet nie powinieneś tego robić, chyba że masz nieodparty powód, aby nie uruchamiać zadania podczas jego tworzenia; Jeśli chcesz rozpocząć od razu, powinieneś użyć Task.Run lub Task.Factory.StartNew do utworzenia i uruchomienia nowego Task.

Więc, teraz wiemy, aby po prostu pozbyć się tego brzydkiego Start. Uruchomisz swój kod i zobaczysz, że okno wiadomości jest wyświetlane od razu, a nie 5 sekund później, o co chodzi?

Cóż, Task.Delay wystarczy, że wykonasz zadanie, które zakończy się za 5 sekund. Nie zatrzymuje on wykonywania wątku przez 5 sekund. To, co chcesz zrobić, to mieć kod, który zostanie wykonany po zakończeniu tego zadania. Po to jest ContinueWith. Pozwala uruchomić kod po wykonaniu zadania:

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

To będzie działać zgodnie z oczekiwaniami.

Możemy również wykorzystać C# 5.0 w await słowa kluczowego łatwiej dodać kontynuacje:

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

Chociaż pełne wyjaśnienie tego, co się tutaj dzieje jest poza zakresem tego pytania, wynik końcowy jest metoda który zachowuje się bardzo podobnie do poprzedniej metody; pokaże to okno komunikatu 5 sekund po wywołaniu metody, ale sama metoda natychmiast wróci [prawie] w obu przypadkach. To powiedziawszy, await jest bardzo potężny i pozwala nam pisać metody, które wydają się proste i proste, ale byłoby to o wiele trudniejsze i bardziej niepisane do napisania bezpośrednio przy użyciu ContinueWith. To także znacznie upraszcza obsługę błędów, zabierając dużo kodu standardowego.

1

Spróbuj tego.

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

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

Jak Servy powiedział, zadanie już się rozpoczęła, więc wszystko, co pozostało do zrobienia, to czekać na niego (.Wait()):

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

Wywołanie 'Wait()' na zadaniu zablokuje bieżący wątek do momentu rozwiązania zadania. To prawie nigdy nie jest tak, jak chcesz. – Jeremy

+0

@Jeremy: Rzeczywiście, warto zwrócić uwagę na zachowanie, o którym wspomniałeś, ale w tym przypadku jego FunctionA już blokuje bieżący wątek, więc założyłem, że szuka sposobu, aby ustalić, kiedy zadanie zostało zakończone. Aby wyjaśnić różnicę między Wait i async (dla przyszłych czytelników) przeczytaj ten link (http://stackoverflow.com/questions/9519414/whats-the-difference-between-task-start-wait-and-async- czekać na) – Sergiu

Powiązane problemy