Task.Delay()和新任务(()=> Thread.Sleep())之间的区别

我正在整理一个小小的演示,以便使用Thread.Sleep()模拟一个长时间运行的方法,并希望添加异步以轻松地从同步过程转到异步过程。 这是初始代码:

 private void button1_Click(object sender, EventArgs e) { LongProcess(); } private void LongProcess() { for (int i = 0; i < 33; i++) { progressBar1.Value += 3; Thread.Sleep(1000); } progressBar1.Value += 1; } 

我以为我可以简单地将Thread.Sleep(1000)更改为new Task(()=>Thread.Sleep(1000)) ,如下所示:

 private void button1_Click(object sender, EventArgs e) { LongProcess(); } private async void LongProcess() { for (int i = 0; i Thread.Sleep(1000)); } progressBar1.Value += 1; } 

然而,在第一次等待之后,这永远不会返回到循环。 如果我将Thread.Sleep更改为Task.Delay一切正常,但我不明白为什么我的代码不起作用。 我假设某些东西永远被封锁,但它没有多大意义。 任何人都可以解释我的代码如何工作,以及一个可能的解决方案,而无需更改为Task.Delay(只是这样我可以得到另一个视角,如何工作)?

Task.Delay与使用Thread.Sleep任务不同。 Task.Delay内部使用Timer ,因此它不会阻塞任何线程,但是使用Thread.Sleep启动一个新的任务会阻塞该线程(通常是Threadpool线程)。

在您的示例中,您从未启动过Task 。 使用构造函数创建Task将返回需要通过调用Start方法启动的Unstarted任务。 否则它永远不会完成(因为你从未开始它)。

但是不鼓励调用Task.Start ,如果你想浪费一个 ,你可以调用Task.Factory.StartNew(()=> Thread.Sleep(1000))Task.Run(()=> Thread.Sleep(1000)) 资源

此外,请注意StartNew是危险的 ,除了有令人信服的理由之外,你应该更喜欢Task.Run不是StartNew

new Task(()=>Thread.Sleep(1000))创建一个Task,但不启动它。

您可以使用Task.Run(() => Thread.Sleep(1000))Task.Factory.StartNew(() => Thread.Sleep(1000))来创建和启动任务。

要回答问题标题 – Task.Delay是可取消的!

考虑使用TaskCompletionSource的流行实现。

 static Task Delay(int delayTime, System.Threading.CancellationToken token) { TaskCompletionSource tcs = new TaskCompletionSource(); if (delayTime < 0) throw new ArgumentOutOfRangeException("Delay time cannot be under 0"); System.Threading.Timer timer = null; timer = new System.Threading.Timer(p => { timer.Dispose(); //stop the timer tcs.TrySetResult(null); //timer expired, attempt to move task to the completed state. }, null, delayTime, System.Threading.Timeout.Infinite); token.Register(() => { timer.Dispose(); //stop the timer tcs.TrySetCanceled(); //attempt to mode task to canceled state }); return tcs.Task; } 

你不能用Thread.Sleep做到这一点。 你可以使用一个很大的旧循环,但它只是在上面的代码中模拟底层的Timer