Difference between Task.Delay() and new Task(()=>Thread.Sleep())

人盡茶涼 提交于 2020-01-11 09:44:10

问题


I was putting together a small little demo to take a long running method simulated with a Thread.Sleep() and wanted to add async to easily go from a synchronous process to an async one. Here's the initial code:

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

I was thinking I could simply change the Thread.Sleep(1000) into a new Task(()=>Thread.Sleep(1000)), like so:

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

However this never returns to the loop after the first await. If I change the Thread.Sleep to a Task.Delay everything works, but I don't understand why my code doesn't work. I assume something is getting blocked forever, but it doesn't quite make sense. Can anyone explain how my code works, and a possible solution without changing to Task.Delay (just so I can get another perspective of how this works)?


回答1:


Task.Delay isn't same as staring a Task with Thread.Sleep. Task.Delay uses Timer internally and thus it doesn't blocks any thread, however starting a new Task with Thread.Sleep blocks the thread (typically Threadpool thread).

In your example you never started the Task. Creating a Task with constructor will return a Unstarted task needs to be started with a call to Start method. Otherwise it will never complete(because you never started it).

However calling Task.Start is discouraged, You can call Task.Factory.StartNew(()=> Thread.Sleep(1000)) or Task.Run(()=> Thread.Sleep(1000)) if you want to waste a resource.

Also, be aware that StartNew is dangerous, you should prefer Task.Run over StartNew unless there's a compelling reason to do so.




回答2:


new Task(()=>Thread.Sleep(1000)) creates a Task, but doesn't start it.

You can use Task.Run(() => Thread.Sleep(1000)) or Task.Factory.StartNew(() => Thread.Sleep(1000)) to create and start a task.




回答3:


To answer the question title - Task.Delay is cancellable!

Consider a popular implementation using 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;
    }

You cannot do this with Thread.Sleep. You can do it with a big old loop but that simply emulates the underlying Timer in the code above.



来源:https://stackoverflow.com/questions/27820626/difference-between-task-delay-and-new-task-thread-sleep

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!