Apakah prevTask.Wait () direkomendasikan untuk digunakan dengan ContinueWith (dari pustaka Tasks)?


88

Jadi saya baru-baru ini diberi tahu bahwa cara saya menggunakan .ContinueWith untuk Tasks bukanlah cara yang tepat untuk menggunakannya. Saya belum menemukan buktinya di internet jadi saya akan bertanya kepada kalian dan melihat apa jawabannya. Berikut adalah contoh cara saya menggunakan .ContinueWith:

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });
}

Sekarang saya tahu ini adalah contoh sederhana dan akan berjalan sangat cepat, tetapi anggap saja setiap tugas melakukan operasi yang lebih lama. Jadi, apa yang saya diberitahu adalah bahwa di .ContinueWith, Anda perlu mengatakan prevTask.Wait (); jika tidak, Anda bisa melakukan pekerjaan sebelum tugas sebelumnya selesai. Apakah itu mungkin? Saya berasumsi tugas kedua & ketiga saya hanya akan berjalan setelah tugas sebelumnya selesai.

Apa yang saya diberitahu bagaimana menulis kode:

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 3");
    });
}

Jawaban:


115

Ehhh .... Saya pikir beberapa jawaban saat ini kehilangan sesuatu: apa yang terjadi dengan pengecualian?

Satu-satunya alasan Anda memanggil Waitsebuah kelanjutan adalah untuk mengamati pengecualian potensial dari anteseden dalam kelanjutan itu sendiri. Pengamatan yang sama akan terjadi jika Anda mengakses Resultdalam kasus a Task<T>dan juga jika Anda mengakses Exceptionproperti secara manual . Terus terang, saya tidak akan menelepon Waitatau mengakses Resultkarena jika ada pengecualian Anda akan membayar harga untuk menaikkannya kembali yang merupakan biaya overhead yang tidak perlu. Sebagai gantinya Anda bisa memeriksa IsFaultedproperti dari anteseden Task. Alternatifnya, Anda dapat membuat alur kerja bercabang dengan merangkai beberapa lanjutan saudara yang hanya diaktifkan berdasarkan keberhasilan atau kegagalan dengan TaskContinuationOptions.OnlyOnRanToCompletiondan TaskContinuationOptions.OnlyOnFaulted.

Sekarang, Anda tidak perlu mengamati pengecualian anteseden dalam lanjutan, tetapi Anda mungkin tidak ingin alur kerja Anda bergerak maju jika, katakanlah, "Langkah 1" gagal. Dalam hal ini: menentukan panggilan TaskContinuationOptions.NotOnFaultedAnda ContinueWithakan mencegah logika kelanjutan bahkan tidak aktif.

Ingatlah bahwa, jika kelanjutan Anda sendiri tidak mengamati pengecualian, orang yang menunggu alur kerja keseluruhan ini untuk menyelesaikannya akan menjadi orang yang mengamatinya. Entah mereka sedang berada Waitdi Taskhulu atau telah mengikuti kelanjutan mereka sendiri untuk mengetahui kapan itu selesai. Jika yang terakhir, kelanjutannya perlu menggunakan logika observasi yang disebutkan di atas.


2
Akhirnya seseorang memberikan jawaban yang benar. @ Travyguy9 Silakan baca jawaban @DrewMarsh ini dan baca lebih lanjut tentangTaskContinuationOptions
Jasper

2
Jawaban yang bagus, saya sedang mencari "Ingatlah bahwa, jika kelanjutan Anda sendiri tidak mengamati pengecualian, orang yang menunggu alur kerja keseluruhan ini untuk diselesaikan akan menjadi orang yang mengamatinya." Namun satu pertanyaan, ketika tugas Anda tidak menunggu, siapakah pelayan default? (Tidak dapat menemukan jawaban untuk ini)
Thibault D.

20

Anda menggunakannya dengan benar.

Membuat kelanjutan yang dijalankan secara asinkron saat Tugas target selesai.

Sumber: Metode Task.ContinueWith (Tindakan sebagai MSDN)

Harus memanggil prevTask.Wait()dalam setiap Task.ContinueWithpemanggilan tampaknya seperti cara yang aneh untuk mengulangi logika yang tidak perlu - yaitu melakukan sesuatu untuk menjadi "sangat yakin duper" karena Anda sebenarnya tidak memahami apa yang dilakukan sedikit kode tertentu. Seperti memeriksa nol hanya untuk melempar di ArgumentNullExceptiontempat yang seharusnya dilemparkan.

Jadi, tidak, siapa pun yang memberi tahu Anda itu salah dan mungkin tidak mengerti mengapa Task.ContinueWithada.


16

Siapa yang memberitahumu?

Mengutip MSDN :

Membuat kelanjutan yang dijalankan secara asinkron saat Tugas target selesai.

Juga, apa tujuan Continue With jika tidak menunggu tugas sebelumnya selesai?

Anda bahkan dapat mengujinya sendiri:

Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
        Thread.Sleep(2000);
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("I waited step 1 to be completed!");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });

5

Dari MSDN padaTask.Continuewith

Tugas yang dikembalikan tidak akan dijadwalkan untuk dieksekusi hingga tugas saat ini selesai. Jika kriteria yang ditentukan melalui parameter continuationOptions tidak terpenuhi, tugas kelanjutan akan dibatalkan, bukan dijadwalkan.

Saya pikir cara Anda mengharapkannya bekerja pada contoh pertama adalah cara yang benar.



0

Dengan Mengakses Task.ResultAnda sebenarnya melakukan logika yang mirip dengantask.wait


Iya. Kita bisa menghindari metode Wait (). Tapi ini bekerja dengan hasil tugas saja, misalnya Tugas <bool>
Alexander Ulmaskulov

Tidak dipilih karena tidak menjawab pertanyaan sebenarnya. Ini menambah nilai, tetapi harus menjadi komentar.
Sinaesthetic

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.