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");
    });
}
Travyguy9
sumber
2
Jangan gunakan StartNew blog.stephencleary.com/2013/08/startnew-is-dangerous.html
Chris Marisic

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.

Drew Marsh
sumber
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.

Anders Arpi
sumber
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");
    });
ken2k
sumber
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.

mclark1129
sumber
2

Anda mungkin juga ingin mempertimbangkan untuk menggunakan Task.Run daripada Task.Factory.StartNew.

Postingan blog Stephen Cleary dan postingan Stephen Toub yang dia referensikan menjelaskan perbedaannya. Ada juga pembahasan dalam jawaban ini .

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

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

Sameh
sumber
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
0

Saya akan mengulangi apa yang sudah dibicarakan banyak orang, prevTask.Wait() tidak perlu .

Untuk contoh lainnya, kita dapat pergi ke Chaining Tasks menggunakan Continuation Tasks , tautan lain oleh Microsoft dengan contoh yang bagus.

Amit Dash
sumber