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