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;
    }