EDIT: Updating LongTask() to be await Task.Delay(2000); makes it work how I need, so thanks for the answers, y'all!
I am trying to call an async function from within a sync function to let the sync function keep going before the async function is finished. I don't know why the code below waits for LongTask() to complete before continuing. I thought it should only do that if I did await LongTask(). Can someone help me understand what I'm missing?
C# fiddle here
using System;
using System.Diagnostics;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
var timer = new Stopwatch();
timer.Start();
LongTask();
timer.Stop();
long ms = timer.ElapsedMilliseconds;
Console.WriteLine(ms.ToString());
}
async static void LongTask()
{
Task.Delay(2000).Wait();
}
}
As already mentioned in the comments:
you have to use await Task.Delay(2000) to tell the compiler that you want run Task.Delay(2000) asynchronous.
If you take a closer look in the example provided by you the compiler shows a warning:
This async methods lacks 'await'operators and will run synchronously [...]
This will hopefully demonstrate it better:
static async Task Main(string[] args)
{
var timer = Stopwatch.StartNew();
var t1 = LongTask();
var t2 = LongTask();
Console.WriteLine("Started both tasks in {0}", timer.Elapsed);
await Task.WhenAll(t1, t2);
timer.Stop();
Console.WriteLine("Tasks finished in {0}", timer.Elapsed);
}
async static Task LongTask()
{
await Task.Delay(2000);
}
Notice the change in return types of functions. Notice the output, too. Both task are waiting for something (in this case delay to expire) in parallel and at the same time, the Main function is able to continue running, printing text and then waiting for the outcome of those functions.
It is important to understand that the execution of an async function runs synchronously until it hits the await operator. If you don't have any await in an async function it will not run asynchronously (and compiler should warn about it). This is applied recursively (if you have several nested awaits) until you hit something that is actually going to be waited for. See example:
static async Task Main(string[] args)
{
var timer = Stopwatch.StartNew();
var t1 = LongTask(1);
Console.WriteLine("t1 started in {0}", timer.Elapsed);
var t2 = LongTask(2);
Console.WriteLine("Started both tasks in {0}", timer.Elapsed);
await Task.WhenAll(t1, t2);
timer.Stop();
Console.WriteLine("Tasks finished in {0}", timer.Elapsed);
}
async static Task LongTask(int id)
{
Console.WriteLine("[{0}] Before subtask 1", id);
await LongSubTask1();
Console.WriteLine("[{0}] Before subtask 2", id);
await LongSubTask2();
Console.WriteLine("[{0}] After subtask 1", id);
}
async static Task LongSubTask1(int id)
{
Console.WriteLine("[{0}] LongSubTask1", id);
await Task.Delay(1000);
}
async static Task LongSubTask2(int id)
{
Console.WriteLine("[{0}] LongSubTask2", id);
await Task.Delay(1000);
}
If you run it, you will see that the execution runs synchronously all the way down to Task.Delay call and only then it returns all the way back to the Main function.
In order to benefit from async/await, you have to use it all the way down to where it ends up calling some I/O (network, disk, etc.) you will need to wait for (or some other event that does not require constant polling to figure out, or artificially created like delay in that sample). If you need to do some expensive computation (that does not have anything async in it) without occupying some particular thread (say, UI thread in GUI application), you'd need to explicitly start new task with it, which at some later point you can await for to get its result.