Here is the code:
static async Task Main(string[] args)
{
var t = new Task(async () => await AsyncTest());
t.Start();
t.Wait();
Console.WriteLine("Main finished");
}
private static async Task AsyncTest()
{
Thread.Sleep(2000);
await Task.Delay(2000);
Console.WriteLine("Method finished");
}
My expectation is that t.Wait() will actually wait for AsyncTest method completion and output will be:
Method finished
Main finished
In reality output has only Main finished. Wait() is completed right at the moment when you hit await Task.Delay(2000) inside AsyncTest. The same happens if you replace t.Wait() with await t / await Task.WhenAll(t) / Task.WaitAll(t).
The solution is to rewrite method to synchronous implementation or call Wait() directly on AsyncTest(). However, the question is why does it work in such strange way?
P.S. It's a simplified version of code. I was trying to achieve deferred task execution. In reality Task object is created by one part of program and then later is executed by another part after some specific condition.
UPD: Rewriting var t = new Task(async () => await AsyncTest()) to var t = new Task(()=> AsyncTest().Wait()) also fixes the problem. Though I still don't quite understand why Task doesn't work correctly with async/await inside delegate.
I still don't quite understand why Task doesn't work correctly with async/await inside delegate.
Because the Task constructor is only used for creating Delegate Tasks - i.e., tasks that represent synchronous code to be run. Since the code is synchronous, your async lambda is being treated as an async void lambda, which means the Task instance won't asynchronously wait for AsyncTest to complete.
More to the point, the Task constructor should never, ever be used in any code, anywhere, for any reason. It has literally zero valid use cases.
A good replacement for Task.Task is Task.Run, which does understand async lambdas.
In my real program Task has deferred execution. Task object is created in one place, and then later after specific condition executed by another part of program.
In that case, use an asynchronous delegate. Specifically, Func<Task>.
static async Task Main(string[] args)
{
Func<Task> func = AsyncTest;
// Later, when we're ready to run.
await func();
Console.WriteLine("Main finished");
}
private static async Task AsyncTest()
{
Thread.Sleep(2000);
await Task.Delay(2000);
Console.WriteLine("Method finished");
}
A quote from @JonSkeet:
Your async method just returns void, which means there's no simple way of anything waiting for it to complete. (You should almost always avoid using async void methods. They're really only available for the sake of subscribing to events.)
So look at this line of your code:
var t = new Task(async () => await AsyncTest());
Look at the signature of Task constructors:
public Task(Action action);
public Task(Action action, CancellationToken cancellationToken);
public Task(Action action, TaskCreationOptions creationOptions);
public Task(Action<object> action, object state);
public Task(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions);
public Task(Action<object> action, object state, CancellationToken cancellationToken);
public Task(Action<object> action, object state, TaskCreationOptions creationOptions);
public Task(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions);
All of them are Actions and as you know Action has void return type.
static async Task Main(string[] args)
{
// best way to do it
await AsyncTest();
Console.WriteLine("Main finished");
}
private static async Task AsyncTest()
{
// Don't use thread sleep, await task delay is fine
// Thread.Sleep(2000);
await Task.Delay(2000);
Console.WriteLine("Method finished");
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With