Kapan harus menggunakan Task.Delay, kapan harus menggunakan Thread. Tidur?

386

Apakah ada aturan yang baik untuk kapan menggunakan Task.Delay versus Thread.Tidur ?

  • Secara khusus, adakah nilai minimum untuk menyediakan agar satu menjadi efektif / efisien di atas yang lain?
  • Terakhir, karena Task.Delay menyebabkan pengalihan konteks pada mesin status async / wait, apakah ada overhead untuk menggunakannya?
Tom K.
sumber
2
10ms adalah banyak siklus di dunia komputer ...
Brad Christie
Seberapa cepat seharusnya? Masalah kinerja apa yang Anda miliki?
LB
4
Saya pikir pertanyaan yang lebih relevan adalah dalam konteks apa Anda bermaksud menggunakan salah satu dari ini? Tanpa informasi itu ruang lingkupnya terlalu luas. Apa yang Anda maksud dengan efektif / efisien? Apakah Anda mengacu pada akurasi, efisiensi daya, dll.? Saya sangat ingin tahu dalam konteks apa ini penting.
James World
4
Minimumnya adalah 15,625 msec, nilai-nilai kurang dari laju interupsi jam tidak berpengaruh. Tugas. Selalu membakar Sistem .Threading.Timer, Tidur tidak memiliki overhead. Anda tidak perlu khawatir tentang overhead ketika Anda menulis kode yang tidak melakukan apa-apa.
Hans Passant
sesuatu yang saya tidak lihat disebutkan, tetapi saya pikir itu penting, akan menjadi Task.Delay mendukung PembatalanToken, yang berarti Anda dapat mengganggu penundaan, jika Anda, misalnya, menggunakannya untuk memperlambat proses siklus. ini juga berarti proses Anda dapat merespons dengan cepat ketika Anda ingin membatalkannya. tetapi Anda dapat mencapai hal yang sama dengan Thread. Tidur membuat interval siklus tidur lebih pendek, dan periksa manuallay Token.
Droa

Jawaban:

370

Gunakan Thread.Sleepsaat Anda ingin memblokir utas saat ini.

Gunakan Task.Delaysaat Anda menginginkan penundaan logis tanpa memblokir utas saat ini.

Efisiensi seharusnya tidak menjadi perhatian utama dengan metode ini. Penggunaan dunia nyata utama mereka adalah sebagai coba lagi timer untuk operasi I / O, yang berada di urutan detik daripada milidetik.

Stephen Cleary
sumber
3
Ini adalah kasus penggunaan utama yang sama: coba lagi timer.
Stephen Cleary
4
Atau ketika Anda tidak ingin mengunyah CPU di loop utama.
Eddie Parker
5
@RoyiNamir: Tidak. Tidak ada "utas lainnya". Secara internal, ini diterapkan dengan timer.
Stephen Cleary
20
Saran untuk tidak khawatir tentang efisiensi adalah keliru. Thread.Sleepakan memblokir utas saat ini yang menyebabkan sakelar konteks. Jika Anda menggunakan kumpulan utas, ini juga dapat menyebabkan utas baru dialokasikan. Kedua operasi cukup berat sedangkan koperasi multi-tugas yang disediakan oleh Task.Delaydll dirancang untuk menghindari semua overhead itu, memaksimalkan throughput, memungkinkan pembatalan, dan menyediakan kode yang lebih bersih.
Corillian
2
@LucaCremry onesi: I would use Thread.Sleep` untuk menunggu di dalam metode sinkron. Namun, saya tidak pernah melakukan ini dalam kode produksi; dalam pengalaman saya, setiap yang Thread.Sleeppernah saya lihat menunjukkan masalah desain yang perlu diperbaiki dengan benar.
Stephen Cleary
243

Perbedaan terbesar antara Task.Delaydan Thread.Sleepadalah yang Task.Delaydimaksudkan untuk berjalan secara tidak sinkron. Tidak masuk akal untuk menggunakan Task.Delaykode sinkron. Ini adalah ide yang SANGAT buruk untuk digunakan Thread.Sleepdalam kode asinkron.

Biasanya Anda akan menelepon Task.Delay() dengan awaitkata kunci:

await Task.Delay(5000);

atau, jika Anda ingin menjalankan beberapa kode sebelum penundaan:

var sw = new Stopwatch();
sw.Start();
Task delay = Task.Delay(5000);
Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
await delay;

Coba tebak ini akan mencetak? Berjalan selama 0,0070048 detik. Jika kita memindahkan yang di await delayatas Console.WriteLine, itu akan mencetak Running selama 5.0020168 detik.

Mari kita lihat perbedaannya dengan Thread.Sleep:

class Program
{
    static void Main(string[] args)
    {
        Task delay = asyncTask();
        syncCode();
        delay.Wait();
        Console.ReadLine();
    }

    static async Task asyncTask()
    {
        var sw = new Stopwatch();
        sw.Start();
        Console.WriteLine("async: Starting");
        Task delay = Task.Delay(5000);
        Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
        await delay;
        Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
        Console.WriteLine("async: Done");
    }

    static void syncCode()
    {
        var sw = new Stopwatch();
        sw.Start();
        Console.WriteLine("sync: Starting");
        Thread.Sleep(5000);
        Console.WriteLine("sync: Running for {0} seconds", sw.Elapsed.TotalSeconds);
        Console.WriteLine("sync: Done");
    }
}

Coba tebak apa yang akan dicetak ini ...

async: Memulai
async: Menjalankan
sinkronisasi 0,0070048 detik : Memulai
async: Menjalankan selama 5,0119008 detik
async: Selesai
sinkronisasi: Menjalankan untuk
sinkronisasi 5,2020168 detik : Selesai

Juga, menarik untuk diperhatikan bahwa Thread.Sleepjauh lebih akurat, akurasi ms tidak benar-benar masalah, sementara Task.Delaydapat mengambil minimal 15-30ms. Overhead pada kedua fungsi minimal dibandingkan dengan akurasi ms yang dimilikinya (gunakan StopwatchKelas jika Anda membutuhkan sesuatu yang lebih akurat).Thread.Sleepmasih mengikat Thread Anda, Task.Delaylepaskan untuk melakukan pekerjaan lain saat Anda menunggu.

Dorus
sumber
15
Mengapa itu "ide yang SANGAT buruk untuk menggunakan Thread. Tidur dalam kode asinkron"?
sunside
69
@sunside Salah satu keuntungan utama dari kode async adalah memungkinkan satu utas bekerja pada banyak tugas sekaligus, dengan menghindari pemblokiran panggilan. Ini menghindari kebutuhan akan sejumlah besar utas individual, dan memungkinkan utas untuk melayani banyak permintaan sekaligus. Namun, mengingat bahwa kode async biasanya berjalan di threadpool, tanpa perlu memblokir satu utas dengan Thread.Sleep()mengkonsumsi seluruh utas yang seharusnya dapat digunakan di tempat lain. Jika banyak tugas dijalankan dengan Thread.Sleep (), ada kemungkinan besar melelahkan semua utas threadpool dan secara serius menghambat kinerja.
Ryan
1
Mendapatkan. Saya kehilangan pengertian kode asinkron dalam arti asyncmetode karena mereka didorong untuk digunakan. Ini pada dasarnya hanya ide buruk untuk dijalankan Thread.Sleep()di utas threadpool, bukan ide buruk pada umumnya. Lagi pula, ada TaskCreationOptions.LongRunningsaat pergi rute (meskipun berkecil hati) Task.Factory.StartNew().
sunside
6
pujian untukawait wait
Eric Wu
2
@Reyhn Dokumentasi tentang ini adalah yang Tasl.Delaymenggunakan timer sistem. Karena "Jam sistem" terus-menerus "pada laju yang konstan.", Kecepatan tick dari timer sistem adalah sekitar 16 ms, setiap penundaan yang Anda minta akan dibulatkan ke sejumlah tick dari jam sistem, diimbangi dengan waktu hingga yang pertama kutu. Lihat dokumentasi Task.Delay msdn di docs.microsoft.com/en-us/dotnet/api/… dan gulir ke bawah untuk komentar.
Dorus
28

jika utas saat ini terbunuh dan Anda gunakan Thread.Sleepdan sedang dieksekusi maka Anda mungkin mendapatkan ThreadAbortException. Dengan Task.DelayAnda selalu dapat memberikan token pembatalan dan dengan anggun membunuhnya. Itulah salah satu alasan saya akan memilih Task.Delay. lihat http://social.technet.microsoft.com/wiki/contents/articles/21177.visual-c-thread-sleep-vs-task-delay.aspx

Saya juga setuju efisiensi bukan yang terpenting dalam kasus ini.

Balanika
sumber
2
Asumsikan kita punya situasi berikut: await Task.Delay(5000). Ketika saya membunuh tugas yang saya dapatkan TaskCanceledException(dan menekannya) tetapi utas saya masih hidup. Rapi! :)
AlexMelw
24

Saya ingin menambahkan sesuatu. Sebenarnya, Task.Delaymekanisme menunggu berbasis timer. Jika Anda melihat sumbernya, Anda akan menemukan referensi ke Timerkelas yang bertanggung jawab atas keterlambatan tersebut. Di sisi lain Thread.Sleepsebenarnya membuat utas saat ini tertidur, dengan cara itu Anda hanya memblokir dan membuang satu utas. Dalam model pemrograman async Anda harus selalu menggunakan Task.Delay()jika Anda menginginkan sesuatu (kelanjutan) terjadi setelah beberapa penundaan.

crypted
sumber
'menunggu Task.Delay ()' membebaskan utas untuk melakukan hal-hal lain sampai timer berakhir, 100% jelas. Tetapi bagaimana jika saya tidak dapat menggunakan 'menunggu' karena metode ini tidak diawali dengan 'async'? Maka saya hanya bisa memanggil 'Task.Delay ()'. Dalam hal ini utas masih diblokir tetapi saya memiliki keuntungan membatalkan Penundaan () . Apakah itu benar?
Erik Stroeken
5
@ErikStroeken Anda dapat memberikan token pembatalan ke utas dan tugas. Task.Delay (). Wait () akan memblokir, sementara Task.Delay () hanya membuat tugas jika digunakan tanpa menunggu. Apa yang Anda lakukan dengan tugas itu terserah Anda, tetapi utas berlanjut.