Thread.Sleep vs Task.Delay

一、概述

Thread.Sleep()和Task.Delay()可以暂停程序(线程)的执行。但是实质是暂停执行吗?两者有什么区别?如何从休眠线程或已等待的任务中止?通过下面示例来进行说明,创建一个简单的winform程序,界面如下:
有以下两种基本的辅助方法PutThreadSleep和PutTaskDelay,然后分别调用这两个方法:
     private void BtnThreadSleep_Click(object sender, EventArgs e)
        {
            PutThreadSleep();
            MessageBox.Show("I am back");
        }

        private async void BtnTaskDelay_Click(object sender, EventArgs e)
        {
            await PutTaskDelay();
            MessageBox.Show("I am back");
        }     

        void PutThreadSleep()
        {
            Thread.Sleep(5000);
        }

        async Task PutTaskDelay()
        {
            await Task.Delay(5000);
        }
当我分别单击前两个按钮时,消息框将在5秒钟后显示。但是这两种实现之间存在重大差异。具体分析如下:
  • Thread.Sleep()

  这是挂起执行的经典方法。此方法将挂起当前线程,直到经过给定的时间。因为Thread.Sleep挂起了进行调用的线程,并且在按钮事件处理程序中调用Thread.Sleep(在UI线程中运行),因此UI处于休眠状态无响应,效果上来看就是无法拖动窗口了。当调用Thread.Sleep时,除了等待时间过去或通过重新启动应用程序外,您无法做任何其他操作来终止它。 

  • Task.Delay()

  Task.Delay的行为与Thread.Sleep的行为非常不同。基本上,Task.Delay将创建一个任务,该任务将在一段时间后完成。Task.Delay没有阻止调用线程因此UI将保持响应状态。幕后有一个计时器,直到指定的时间为止。由于计时器控制延迟,因此我们可以随时通过停止计时器来取消延迟。修改上面的PutTaskDelay方法,支持取消操作,如下所示:

CancellationTokenSource tokenSource = new CancellationTokenSource();
async Task PutTaskDelay()
{
    try
    {
        await Task.Delay(5000, tokenSource.Token);
    }
    catch (TaskCanceledException ex)
    {

    }
    catch (Exception ex)
    {

    }
}

在对Task.Delay的调用中,我添加了一个取消令牌。当任务被取消时,它将抛出TaskCanceledException。这里仅仅捕捉到异常并加以抑制,并不显示任何有关此的消息。在“取消任务延迟”按钮单击事件处理程序中,通过使用取消令牌来指示要取消的任务:

private void BtnCancelTaskDelay_Click(object sender, EventArgs e)
{
    tokenSource.Cancel();
}

取消任务后,PutTaskDelay方法将立即返回,并且调用方将显示消息框。

  • 完整代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WinFormsApp2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private void BtnThreadSleep_Click(object sender, EventArgs e)
        {
            PutThreadSleep();
            MessageBox.Show("I am back");
        }

        private async void BtnTaskDelay_Click(object sender, EventArgs e)
        {
            await PutTaskDelay();
            MessageBox.Show("I am back");
        }
        private void BtnCancelTaskDelay_Click(object sender, EventArgs e)
        {
            tokenSource.Cancel();
        }

        void PutThreadSleep()
        {
            Thread.Sleep(5000);
        }

        CancellationTokenSource tokenSource = new CancellationTokenSource();
        async Task PutTaskDelay()
        {
            try
            {
                await Task.Delay(5000, tokenSource.Token);
            }
            catch (TaskCanceledException ex)
            {

            }
            catch (Exception ex)
            {

            }
        }
    }
}

二、总结

  • Thread.Sleep 是同步延迟,Task.Delay异步延迟
  • Thread.Sleep 会阻塞线程,Task.Delay不会
  • Thread.Sleep不能取消,Task.Delay可以
  • Task.Delay() 比 Thread.Sleep() 消耗更多的资源,但是Task.Delay()可用于为方法返回Task类型或者根据CancellationToken取消标记来动态取消等待
  • Task.Delay() 实质创建一个运行给定时间的任务, Thread.Sleep() 使当前线程休眠给定时间

三、参考资料

1、https://social.technet.microsoft.com/wiki/contents/articles/21177.visual-c-thread-sleep-vs-task-delay.aspx

原文地址:https://www.cnblogs.com/qtiger/p/14816903.html