C# Task.Delay()和新任务之间的差异(()=>;Thread.Sleep())
我准备了一个小的演示程序,用一个C# Task.Delay()和新任务之间的差异(()=>;Thread.Sleep()),c#,multithreading,asynchronous,thread-sleep,C#,Multithreading,Asynchronous,Thread Sleep,我准备了一个小的演示程序,用一个Thread.Sleep()模拟了一个长时间运行的方法,并希望添加异步,以便轻松地从同步进程过渡到异步进程。以下是初始代码: private void button1_Click(object sender, EventArgs e) { LongProcess(); } private void LongProcess() { for (int i = 0; i < 33; i++) { progressBar1.
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;
}
然而,在第一次等待之后,这永远不会返回到循环。如果我改变线程。睡眠到任务。延迟一切工作,但我不明白为什么我的代码不工作。我想有些东西会永远被阻塞,但这不太合理。谁能解释一下我的代码是如何工作的,以及一个可能的解决方案,而不必更改为Task.Delay(只是为了让我能从另一个角度了解它是如何工作的)?新任务(()=>Thread.Sleep(1000))
创建一个任务,但不启动它
您可以使用
Task.Run(()=>Thread.Sleep(1000))
或Task.Factory.StartNew(()=>Thread.Sleep(1000))
创建和启动任务。任务。延迟与使用Thread.Sleep
启动任务不同<代码>任务。延迟
在内部使用<代码>计时器
,因此它不会阻止任何线程,但是使用<代码>线程启动新任务。睡眠会阻止线程(通常是线程池线程)
在您的示例中,您从未启动任务
。使用构造函数创建任务
将返回一个未启动的
任务,需要通过调用Start
方法启动该任务。否则它将永远不会完成(因为你从未开始过它)
但是不鼓励调用Task.Start
,如果希望浪费资源,可以调用Task.Factory.StartNew(()=>Thread.Sleep(1000))
或Task.Run(()=>Thread.Sleep(1000))
另外,请注意,您应该更喜欢
任务。运行而不是开始新的。回答问题标题-任务。延迟是可以取消的
考虑使用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;
}
静态任务延迟(int delayTime,System.Threading.CancellationToken)
{
TaskCompletionSource tcs=新的TaskCompletionSource();
如果(delayTime<0)抛出新ArgumentOutOfRangeException(“延迟时间不能小于0”);
System.Threading.Timer=空;
定时器=新系统.线程.定时器(p=>
{
timer.Dispose();//停止计时器
tcs.TrySetResult(null);//计时器已过期,请尝试将任务移动到已完成状态。
},null,delayTime,System.Threading.Timeout.Infinite);
令牌.寄存器(()=>
{
timer.Dispose();//停止计时器
tcs.TrySetCanceled();//尝试将任务模式设置为已取消状态
});
返回tcs.Task;
}
您不能使用线程睡眠执行此操作。您可以使用一个大的旧循环来完成,但它只是模拟上面代码中的底层计时器。等待新任务(()=>Thread.Sleep(1000))
这是一种与Thread.Sleep(1000)
相同的“间接”方法。此外,它与.Net 2.0/3.5不兼容,而且当任务的执行委托给UI线程时,它尤其有问题。@欣快,我想现在我在我的回答中通过链接到stephen的博客解决了您的评论。谢谢您提供的信息,我这样做只是为了学术目的,以了解工作中的基本部分,因此我很欣赏额外的信息StartNew
并不危险。“你在把话塞进克利里先生的嘴里。@Gusdor不知道你的意思。你确定你读过斯蒂芬的博客吗?StartNew确实是一个危险的博客标题。读者不仅应该阅读标题,还应该阅读内容。是的,这是有道理的,我认为如果我在等待的话,开头就暗示着。
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;
}