Task.Delay() 和 new Task(()=>Thread.Sleep()) 的区别
Difference between Task.Delay() and new Task(()=>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 < 33; i++)
{
progressBar1.Value += 3;
await new Task(()=>Thread.Sleep(1000));
}
progressBar1.Value += 1;
}
然而,这从来没有 returns 在第一次等待之后进入循环。如果我将 Thread.Sleep 更改为 Task.Delay 一切正常,但我不明白为什么我的代码不起作用。我假设某些东西永远被阻止了,但这不太合理。任何人都可以解释我的代码是如何工作的,以及不更改为 Task.Delay 的可能解决方案(这样我就可以从另一个角度了解它是如何工作的)?
new Task(()=>Thread.Sleep(1000))
创建了一个任务,但没有启动它。
您可以使用 Task.Run(() => Thread.Sleep(1000))
或 Task.Factory.StartNew(() => Thread.Sleep(1000))
创建和启动任务。
Task.Delay
与使用 Thread.Sleep
启动任务不同。 Task.Delay
在内部使用 Timer
,因此它不会阻塞任何线程,但是使用 Thread.Sleep
启动新任务会阻塞线程(通常是 Threadpool 线程)。
在您的示例中,您从未启动 Task
。使用构造函数创建 Task
将 return 需要通过调用 Start
方法来启动 Unstarted
任务。否则它永远不会完成(因为你从未开始它)。
但是不鼓励调用Task.Start
,如果您想浪费资源,可以调用Task.Factory.StartNew(()=> Thread.Sleep(1000))
或Task.Run(()=> Thread.Sleep(1000))
.
此外,请注意 StartNew is dangerous,除非有令人信服的理由,否则您应该更喜欢 Task.Run
而不是 StartNew
。
回答题目- Task.Delay
可取消!
考虑使用 TaskCompletionSource
的流行实现。
static Task Delay(int delayTime, System.Threading.CancellationToken token)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
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
。
我正在整理一个小演示,以采用 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 < 33; i++)
{
progressBar1.Value += 3;
await new Task(()=>Thread.Sleep(1000));
}
progressBar1.Value += 1;
}
然而,这从来没有 returns 在第一次等待之后进入循环。如果我将 Thread.Sleep 更改为 Task.Delay 一切正常,但我不明白为什么我的代码不起作用。我假设某些东西永远被阻止了,但这不太合理。任何人都可以解释我的代码是如何工作的,以及不更改为 Task.Delay 的可能解决方案(这样我就可以从另一个角度了解它是如何工作的)?
new Task(()=>Thread.Sleep(1000))
创建了一个任务,但没有启动它。
您可以使用 Task.Run(() => Thread.Sleep(1000))
或 Task.Factory.StartNew(() => Thread.Sleep(1000))
创建和启动任务。
Task.Delay
与使用 Thread.Sleep
启动任务不同。 Task.Delay
在内部使用 Timer
,因此它不会阻塞任何线程,但是使用 Thread.Sleep
启动新任务会阻塞线程(通常是 Threadpool 线程)。
在您的示例中,您从未启动 Task
。使用构造函数创建 Task
将 return 需要通过调用 Start
方法来启动 Unstarted
任务。否则它永远不会完成(因为你从未开始它)。
但是不鼓励调用Task.Start
,如果您想浪费资源,可以调用Task.Factory.StartNew(()=> Thread.Sleep(1000))
或Task.Run(()=> Thread.Sleep(1000))
.
此外,请注意 StartNew is dangerous,除非有令人信服的理由,否则您应该更喜欢 Task.Run
而不是 StartNew
。
回答题目- Task.Delay
可取消!
考虑使用 TaskCompletionSource
的流行实现。
static Task Delay(int delayTime, System.Threading.CancellationToken token)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
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
。