问题
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