Memulai mungkin tidak disebut sebagai tugas gaya janji. pengecualian akan datang

108

Saya membuat aplikasi desktop wpf sederhana. UI hanya memiliki tombol dan kode dalam file .cs seperti.

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

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

Tapi yang mengejutkan Task.Delay(5000).Start();adalah garis melempar InvalidOperationException:

Memulai mungkin tidak disebut sebagai tugas gaya janji.

Adakah yang bisa membantu mengapa seperti ini?

DJ
sumber

Jawaban:

171

Anda mendapatkan kesalahan itu karena Taskkelas sudah memulai tugas sebelum memberikannya kepada Anda. Anda hanya boleh memanggil Starttugas yang Anda buat dengan memanggil konstruktornya, dan Anda bahkan tidak boleh melakukannya kecuali Anda memiliki alasan kuat untuk tidak memulai tugas saat Anda membuatnya; jika Anda menginginkannya segera dimulai, Anda harus menggunakan Task.Runatau Task.Factory.StartNewkeduanya membuat dan memulai yang baru Task.

Jadi, sekarang kita tahu untuk menyingkirkan masalah itu Start. Anda akan menjalankan kode Anda dan menemukan bahwa kotak pesan langsung ditampilkan, bukan 5 detik kemudian, ada apa dengan itu?

Nah, Task.Delaymemberi Anda tugas yang akan selesai dalam 5 detik. Itu tidak menghentikan eksekusi utas selama 5 detik. Yang ingin Anda lakukan adalah memiliki beberapa kode yang dijalankan setelah tugas itu selesai. Untuk itulah ContinueWith. Ini memungkinkan Anda menjalankan beberapa kode setelah tugas tertentu selesai:

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

Ini akan berperilaku seperti yang diharapkan.

Kami juga dapat memanfaatkan awaitkata kunci C # 5.0 untuk menambahkan kelanjutan dengan lebih mudah:

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

Meskipun penjelasan lengkap tentang apa yang terjadi di sini berada di luar cakupan pertanyaan ini, hasil akhirnya adalah metode yang berperilaku sangat mirip dengan metode sebelumnya; itu akan menampilkan kotak pesan 5 detik setelah Anda memanggil metode tersebut, tetapi metode itu sendiri akan kembali [hampir] segera dalam kedua kasus tersebut. Yang mengatakan, awaitsangat kuat, dan memungkinkan kita untuk menulis metode yang tampak sederhana dan lugas, tetapi itu akan jauh lebih sulit dan lebih berantakan untuk menulis menggunakan ContinueWithsecara langsung. Ini juga sangat menyederhanakan penanganan kesalahan, mengambil banyak kode boilerplate.

Pelayanan
sumber
1

Coba ini.

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

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

Seperti yang dikatakan Servy, tugas sudah dimulai, jadi yang perlu Anda lakukan hanyalah menunggu (.Wait ()):

private void Button_Click_2(object sender, RoutedEventArgs e)
{
    FunctionA();
}
public void FunctionA()
{
    Task.Delay(5000).Wait();
    MessageBox.Show("Waiting Complete");
}
Sergiu
sumber
1
Memanggil Wait()tugas akan memblokir utas saat ini hingga tugas terselesaikan. Itu hampir tidak pernah Anda inginkan terjadi.
Jeremy
1
@ Jeremy: Memang, perlu memperhatikan perilaku yang Anda sebutkan, tetapi dalam kasus ini FunctionA-nya sudah memblokir utas saat ini, jadi saya berasumsi dia hanya mencari cara untuk menentukan kapan tugas telah selesai. Untuk memperjelas perbedaan antara Tunggu dan asinkron (untuk pembaca mendatang) silakan baca tautan
Sergiu