เมื่อใดที่จะใช้ Task.Delay เมื่อใดที่จะใช้งาน Thread.Sleep


385

มีกฎที่ดีหรือไม่ที่จะใช้งานTaskDelayกับThread.Sleepเมื่อใด

  • โดยเฉพาะมีค่าต่ำสุดที่จะให้เพื่อให้มีประสิทธิภาพ / มีประสิทธิภาพเหนืออื่น ๆ ?
  • สุดท้ายเนื่องจาก Task.Delay ทำให้เกิดการสลับบริบทบนเครื่อง async / await มีค่าใช้จ่ายหรือไม่

2
10ms เป็นวัฏจักรในโลกคอมพิวเตอร์มากมาย ...
แบรดคริสตี้

มันควรจะเร็วแค่ไหน? คุณมีปัญหาเรื่องประสิทธิภาพอะไร
LB

4
ฉันคิดว่าคำถามที่เกี่ยวข้องมากขึ้นคือสิ่งใดที่คุณตั้งใจจะใช้ในบริบทเหล่านี้ หากไม่มีข้อมูลดังกล่าวขอบเขตก็กว้างเกินไป คุณหมายถึงอะไรที่มีประสิทธิภาพ / มีประสิทธิภาพ? คุณหมายถึงความแม่นยำประสิทธิภาพการใช้พลังงานเป็นต้น? ฉันอยากรู้มากเกี่ยวกับบริบทนี้
James World

4
ค่าต่ำสุดคือ 15.625 msec ค่าน้อยกว่าอัตราการขัดจังหวะนาฬิกาไม่มีผล Task.Delay เผาผลาญ System.Threading เสมอจับเวลา, Sleep ไม่มีค่าใช้จ่าย คุณไม่ต้องกังวลเกี่ยวกับค่าใช้จ่ายเมื่อคุณเขียนโค้ดที่ไม่ทำอะไรเลย
Hans Passant

บางสิ่งที่ฉันไม่ได้เห็นพูดถึง แต่ฉันคิดว่าเป็นสิ่งสำคัญนั่นคือ Task.Delay รองรับ CancellationToken ซึ่งหมายความว่าคุณสามารถขัดขวางการหน่วงเวลาได้ตัวอย่างเช่นหากคุณกำลังใช้มันเพื่อชะลอกระบวนการรอบ นี่หมายความว่ากระบวนการของคุณสามารถตอบสนองได้อย่างรวดเร็วเมื่อคุณต้องการยกเลิก แต่คุณสามารถทำได้เช่นเดียวกันด้วย Thread.Sleep ทำให้ช่วงรอบการนอนหลับสั้นลงและตรวจสอบ Token manuallay
Droa

คำตอบ:


369

ใช้Thread.Sleepเมื่อคุณต้องการบล็อกเธรดปัจจุบัน

ใช้Task.Delayเมื่อคุณต้องการความล่าช้าแบบลอจิคัลโดยไม่ปิดกั้นเธรดปัจจุบัน

ประสิทธิภาพไม่ควรกังวลอย่างยิ่งกับวิธีการเหล่านี้ การใช้งานจริงของโลกหลักของพวกเขาคือลองใช้ตัวจับเวลาซ้ำสำหรับการดำเนินการ I / O ซึ่งอยู่ในลำดับวินาทีมากกว่ามิลลิวินาที


3
เป็นกรณีการใช้งานหลักเดียวกัน: ตัวจับเวลาลองใหม่
Stephen Cleary

4
หรือเมื่อคุณไม่ต้องการเคี้ยว CPU ในลูปหลัก
Eddie Parker

5
@RoyiNamir: ไม่ไม่มี "หัวข้ออื่น" ภายในจะดำเนินการด้วยตัวจับเวลา
Stephen Cleary

20
ข้อเสนอแนะที่ไม่ต้องกังวลเกี่ยวกับประสิทธิภาพนั้นไม่ควรปฏิบัติ Thread.Sleepจะบล็อกเธรดปัจจุบันซึ่งทำให้เกิดการสลับบริบท หากคุณใช้พูลเธรดนี่อาจทำให้เธรดใหม่ได้รับการจัดสรร การดำเนินการทั้งสองค่อนข้างหนักในขณะที่การทำงานแบบมัลติทาสกิ้งที่จัดทำโดยTask.Delayฯลฯ ได้รับการออกแบบมาเพื่อหลีกเลี่ยงค่าใช้จ่ายทั้งหมดเพิ่มปริมาณงานให้มากที่สุดอนุญาตให้ยกเลิกและให้รหัสที่ชัดเจนขึ้น
Corillian

2
@LucaCremry onesi: I would use Thread.Sleep` เพื่อรอวิธีการแบบซิงโครนัส อย่างไรก็ตามฉันไม่เคยทำเช่นนี้ในรหัสการผลิต; จากประสบการณ์ของฉันทุกคนที่Thread.Sleepฉันเคยเห็นได้บ่งบอกถึงปัญหาการออกแบบบางประเภทที่ต้องได้รับการแก้ไขอย่างเหมาะสม
Stephen Cleary

243

ความแตกต่างที่ใหญ่ที่สุดระหว่างTask.DelayและThread.Sleepนั่นTask.Delayก็เพื่อให้ทำงานแบบอะซิงโครนัส มันไม่สมเหตุสมผลที่จะใช้Task.Delayในรหัสซิงโครนัส มันเป็นความคิดที่ดีมากที่จะใช้Thread.Sleepในโค้ดแบบอะซิงโครนัส

โดยปกติแล้วคุณจะโทรหาTask.Delay() ด้วยawaitคำหลัก:

await Task.Delay(5000);

หรือถ้าคุณต้องการเรียกใช้รหัสบางอย่างก่อนที่จะล่าช้า:

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

คาดเดาสิ่งที่จะพิมพ์นี้ วิ่งเป็นเวลา 0.0070048 วินาที ถ้าเราย้ายawait delayข้างบนConsole.WriteLineแทนมันจะพิมพ์ทำงานเป็นเวลา 5.0020168 วินาที

ลองดูความแตกต่างด้วย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");
    }
}

พยายามทำนายสิ่งที่จะพิมพ์ ...

async: การเริ่มต้น
async: การทำงานสำหรับการ
ซิงค์0.0070048 วินาที: การเริ่มต้น
async: การทำงานสำหรับ 5.0119008 วินาที
async: เสร็จสิ้นการ
ซิงค์: ทำงานสำหรับการ
ซิงค์5.0020168 วินาที: เสร็จแล้ว

นอกจากนี้ยังเป็นที่น่าสนใจที่จะสังเกตว่าThread.Sleepมีความแม่นยำมากกว่านั้นความแม่นยำของ ms ไม่ใช่ปัญหาจริงๆในขณะที่Task.Delayใช้เวลาน้อยที่สุด 15-30ms ค่าใช้จ่ายของทั้งสองฟังก์ชั่นนั้นน้อยมากเมื่อเทียบกับความแม่นยำของ ms ที่มี (ใช้Stopwatchคลาสถ้าคุณต้องการบางอย่างที่แม่นยำกว่า) Thread.Sleepยังคงผูกด้ายของคุณTask.Delayปล่อยมันไปทำงานอื่น ๆ ในขณะที่คุณรอ


15
เหตุใดจึงเป็น "เป็นความคิดที่ดีมากที่จะใช้ Thread.Sleep ในโค้ดแบบอะซิงโครนัส"?
ซันไซด์

69
@sunside หนึ่งในข้อดีที่สำคัญของรหัส async คือการอนุญาตให้หนึ่งเธรดสามารถทำงานได้หลายงานพร้อมกันโดยหลีกเลี่ยงการบล็อกการโทร สิ่งนี้หลีกเลี่ยงความต้องการเธรดแต่ละเธรดจำนวนมากและอนุญาตให้เธรดพูลให้บริการคำร้องขอจำนวนมากพร้อมกัน อย่างไรก็ตามเนื่องจากรหัส async มักจะทำงานบนเธรดพูลการบล็อกเธรดเดี่ยวโดยไม่จำเป็นต้องThread.Sleep()ใช้ทั้งเธรดซึ่งสามารถใช้ที่อื่นได้ หากงานจำนวนมากรันด้วย Thread.Sleep () มีโอกาสสูงที่จะหมดเธรดพูลเธรดทั้งหมดและขัดขวางประสิทธิภาพการทำงานอย่างจริงจัง
Ryan

1
รับไปเลย ฉันพลาดแนวคิดเกี่ยวกับโค้ดอะซิงโครนัสในแง่ของasyncวิธีการที่พวกเขาได้รับการสนับสนุนให้ใช้ เป็นเพียงความคิดที่ไม่ดีที่จะเรียกใช้Thread.Sleep()ในเธรดพูลด้ายไม่ใช่ความคิดที่ไม่ดีโดยทั่วไป หลังจากที่ทุกคนมีTaskCreationOptions.LongRunningเมื่อการไป (แม้จะท้อแท้) Task.Factory.StartNew()เส้นทาง
ซันไซด์

6
ความรุ่งโรจน์ของawait wait
Eric Wu

2
@Reyhn เอกสารประกอบในส่วนนี้Tasl.Delayใช้ตัวจับเวลาระบบ ตั้งแต่ "นาฬิกาของระบบ" ติ๊ก "ที่อัตราคงที่" ความเร็วของติ๊กของตัวจับเวลาระบบจะอยู่ที่ประมาณ 16ms ความล่าช้าใด ๆ ที่คุณร้องขอจะถูกปัดเศษเป็นจำนวนของสัญญาณของนาฬิการะบบถูกชดเชยด้วยเวลาจนถึงครั้งแรก เห็บ ดูเอกสารประกอบของTask.Delay msdnในdocs.microsoft.com/en-us/dotnet/api/…และเลื่อนลงไปที่หมายเหตุ
Dorus

28

ถ้าเธรดปัจจุบันถูกฆ่าตายและคุณใช้และจะมีการดำเนินการแล้วคุณอาจได้รับThread.Sleep ThreadAbortExceptionด้วยTask.Delayคุณสามารถให้โทเค็นการยกเลิกและฆ่ามันได้อย่างสง่างาม Thats Task.Delayเหตุผลหนึ่งที่ผมจะเลือก ดูhttp://social.technet.microsoft.com/wiki/contents/articles/21177.visual-c-thread-sleep-vs-task-delay.aspx

ฉันเห็นด้วยกับประสิทธิภาพที่ไม่สำคัญยิ่งในกรณีนี้


2
สมมติว่าเราได้รับสถานการณ์ต่อไปนี้: await Task.Delay(5000). เมื่อฉันฆ่าภารกิจที่ฉันได้รับTaskCanceledException(และปราบปราม) แต่หัวข้อของฉันยังมีชีวิตอยู่ เรียบร้อย! :)
AlexMelw

24

ฉันต้องการเพิ่มบางสิ่ง จริงๆแล้วTask.Delayมันเป็นกลไกการรอตามเวลา หากคุณดูที่แหล่งที่มาคุณจะพบการอ้างอิงไปยังTimerคลาสที่รับผิดชอบความล่าช้า ในทางกลับกันThread.Sleepทำให้เธรดปัจจุบันเข้าสู่โหมดสลีในแบบที่คุณเป็นเพียงการบล็อกและการสูญเสียเธรดหนึ่ง ในรูปแบบการเขียนโปรแกรม async คุณควรใช้Task.Delay()ถ้าคุณต้องการบางสิ่งบางอย่าง (ต่อเนื่อง) เกิดขึ้นหลังจากความล่าช้า


'await Task.Delay ()' ปล่อยเธรดให้ทำสิ่งอื่น ๆ จนกว่าตัวจับเวลาจะหมดอายุ 100% ชัดเจน แต่ถ้าฉันไม่สามารถใช้ 'คอย' เนื่องจากวิธีนี้ไม่ได้ขึ้นต้นด้วย 'async'? จากนั้นฉันจะเรียก 'Task.Delay ()' เท่านั้น ในกรณีที่ว่าด้ายยังคงถูกปิดกั้นแต่ผมมีข้อได้เปรียบของการยกเลิกการหน่วงเวลา () ถูกต้องไหม
Erik Stroeken

5
@ErikStroeken คุณสามารถส่งโทเค็นการยกเลิกไปยังทั้งเธรดและงานได้ Task.Delay (). Wait () จะบล็อกในขณะที่ Task.Delay () เพียงแค่สร้างงานหากใช้โดยไม่รอ สิ่งที่คุณทำกับภารกิจนั้นขึ้นอยู่กับคุณ แต่เธรดดำเนินการต่อ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.