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

时间:2015-01-07 13:30:29

标签: 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.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;
}

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

3 个答案:

答案 0 :(得分:6)

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

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

但不鼓励拨打Task.Start,如果您想浪费资源,可以致电Task.Factory.StartNew(()=> Thread.Sleep(1000))Task.Run(()=> Thread.Sleep(1000))

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

答案 1 :(得分:4)

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

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

答案 2 :(得分:3)

要回答问题标题,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