一、概述
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() 使当前线程休眠给定时间