Başlangıç, vaat tarzı bir görev için çağrılmayabilir. istisna geliyor


109

Basit bir wpf masaüstü uygulaması oluşturuyorum. Kullanıcı arayüzünde .cs dosyasında .cs dosyası gibi yalnızca bir düğme ve kod bulunur.

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

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

Ancak şaşırtıcı bir şekilde satır Task.Delay(5000).Start();bir InvalidOperationException:

Başlangıç, vaat tarzı bir görev için çağrılmayabilir.

Bunun neden böyle olduğuna kimse yardım edebilir mi?

Yanıtlar:


171

Bu hatayı alıyorsunuz çünkü Tasksınıf görevi size vermeden önce zaten başlattı. Yalnızca Startkurucusunu çağırarak oluşturduğunuz bir görevi çağırmalısınız ve görevi oluştururken başlatmamak için ikna edici bir nedeniniz yoksa bunu bile yapmamalısınız; hemen başlamasını istiyorsanız, Task.Runya da Task.Factory.StartNewyeni bir tane oluşturmak ve başlatmak için kullanmalısınız Task.

Artık o sinir bozucudan kurtulmamız gerektiğini biliyoruz Start. Kodunuzu çalıştırırsınız ve mesaj kutusunun 5 saniye sonra değil, hemen gösterildiğini görürsünüz, bunda ne var?

Eh, Task.Delaysadece seni 5 saniye içinde tamamlanacak bir görevi verir. İş parçacığının yürütülmesini 5 saniye durdurmaz. Yapmak istediğiniz şey, bu görev bittikten sonra çalıştırılan bir koda sahip olmaktır. Bunun ContinueWithiçin. Belirli bir görev tamamlandıktan sonra bazı kodları çalıştırmanıza izin verir:

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

Bu beklendiği gibi davranacaktır.

awaitDevamları daha kolay eklemek için C # 5.0'ın anahtar kelimesini de kullanabiliriz :

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

Burada neler olup bittiğine dair tam bir açıklama bu sorunun kapsamı dışındayken, sonuç, önceki yönteme çok benzer davranan bir yöntemdir; yöntemi çağırdıktan 5 saniye sonra bir mesaj kutusu gösterecek, ancak her iki durumda da yöntemin kendisi [neredeyse] hemen dönecektir. Bununla birlikte await, çok güçlüdür ve basit ve anlaşılır görünen yöntemler yazmamıza izin verir, ancak bunu ContinueWithdoğrudan kullanarak yazmak çok daha zor ve daha karmaşık olur . Ayrıca, çok sayıda standart kod çıkararak, hata işlemeyi büyük ölçüde basitleştirir.


1

Bunu dene.

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

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

-4

Servy'nin dediği gibi, görev çoktan başladı, bu yüzden tek yapmanız gereken beklemek (.Wait ()):

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

1
Wait()Bir göreve çağrı yapmak , görev çözülene kadar mevcut iş parçacığını engeller. Neredeyse asla olmasını istediğiniz şey bu değil.
Jeremy

1
@Jeremy: Aslında, bahsettiğiniz davranışa dikkat etmeye değer, ancak bu durumda FunctionA zaten mevcut iş parçacığını engelliyordu, bu yüzden sadece görevin ne zaman tamamlandığını belirlemenin bir yolunu aradığını varsaydım. Bekle ve eşzamansız (gelecekteki okuyucular için) arasındaki farkı açıklığa kavuşturmak için lütfen bu bağlantıyı
Sergiu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.