I'm hoping someone can help me understand what seems like some straightforward code. Clearly, I'm missing something. There are 2 things I don't understand about the behavior of this code:
If I let the code run, the last
Debug.WriteLine
for 'End Delay' never gets written. Why is this?If I put a breakpoint on the
Task.WaitAll()
line in Main(), I see thatdelayTask
has a status ofWaitingToRun
and the 'Begin Delay'Debug.WriteLine
inDoDelay()
doesn't happen, no matter how long I wait. However, as soon as I F10 over the Task.WaitAll() line, I see 'Begin Delay' show up in output. Why does a breakpoint on theTask.WaitAll()
line seems to prevent even beginning the task?
I'm running on .NET Framework 4.6.2, unfortunately, I don't have a choice about this.
Thanks to everyone who has posted. I have an answer for the 1st question above - I changed 'async void DoDelay()' to 'async Task DoDelay()'. But I still don't have any explanation for the behavior in my 2nd question.
static void Main(string[] args)
{
Debug.WriteLine($"Main-A: {DateTime.Now:mm:ss.fff}");
Task delayTask = Task.Run(() => DoDelay(10000));
Debug.WriteLine($"Main-B: {DateTime.Now:mm:ss.fff}");
Task.WaitAll(delayTask);
}
static async void DoDelay(int delayMs)
{
Debug.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
await Task.Delay(delayMs);
Debug.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
CodePudding user response:
Task delayTask = Task.Run(() => DoDelay(10000));
This will call the method DoDelay(10000)
on a background thread, and return a task that completes when the method returns. However, DoDelay
will return at the first await
statement. The rest of the method will be run at some later time, but there is nothing that will wait for this to occur.
To fix this you should make your method return a Task, i.e. async Task DoDelay(int delayMs)
. This will not affect when the method returns, but the returned task lets you wait for the entire method to complete. This can be made more simple by using a async main method:
static async Task Main(string[] args) {
Debug.WriteLine($"Main-A: {DateTime.Now:mm:ss.fff}");
var delayTask = DoDelay(10000);
Debug.WriteLine($"Main-B: {DateTime.Now:mm:ss.fff}");
await delayTask;
Debug.WriteLine($"Main-C: {DateTime.Now:mm:ss.fff}");
}
static async Task DoDelay(int delayMs) {
Debug.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
await Task.Delay(delayMs);
Debug.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
Only use async void
when you absolutely have to, i.e. event handlers in an UI, and then make sure you are handling failures of any awaited tasks. In the vast majority of cases, use async Task
or async Task<T>
CodePudding user response:
some Note: avoid using async void because you have to await on it to get result, So I change it to async Task
You have to use delayTask.Wait(); for waiting to complete the task.
Warning: delayTask.Wait()
is bad practice use await delayTask;
instead.
use this instead of your code I do some changes on it:
static void Main(string[] args) {
Debug.WriteLine($"Main-A: {DateTime.Now:mm:ss.fff}");
Task delayTask = Task.Run(async () => await DoDelay(10000));
Debug.WriteLine($"Main-B: {DateTime.Now:mm:ss.fff}");
delayTask.Wait();
}
static async Task DoDelay(int delayMs)
{
Debug.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
await Task.Delay(delayMs);
Debug.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
you can also use
static async Task Main(string[] args)
and remove Task.Run(async () => await DoDelay(10000));
and use await DoDelay(10000)
and await delayTask;
but it may not work it .NET framework 4.6.2
CodePudding user response:
The correct implementation of the async/await
pattern in this case is as the code below
static class Program
{
static async Task Main(string[] args)
{
Console.WriteLine($"Main-A: {DateTime.Now:mm:ss.fff}");
Task delayTask = Task.Run(async () => { await DoDelay(10000); });
Task delayTaskSync = Task.Run(() => { DoDelaySync(10000); });
Console.WriteLine($"Main-B: {DateTime.Now:mm:ss.fff}");
Task.WaitAll(delayTask, delayTaskSync);
}
static async Task DoDelay(int delayMs)
{
Console.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
await Task.Delay(delayMs);
Console.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
static void DoDelaySync(int delayMs)
{
Console.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
Task.Delay(delayMs).Wait();
Console.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
}
Here you can see an implementation with both an async function and a sync function.
The Task.Run()
run a parallel task and inside that task you can use more task with async/await
or use synchronous function.