Reputation: 15010
I am trying to wrap my head around async await
in C#. I have written this small windows console app that has two files.
Downloader.cs
namespace AsyncAwait
{
public class Downloader
{
public async Task DownloadFilesAsync()
{
// In the Real World, we would actually do something...
// For this example, we're just going to print file 0, file 1.
await DownloadFile0();
await DownloadFile1();
}
public async Task DownloadFile0()
{
int count = 0;
while (count < 100)
{
Console.WriteLine("Downloading File {0,1} ----> {1,3}%",0,count);
Thread.Sleep(10);
count++;
}
await Task.Delay(100);
}
public async Task DownloadFile1()
{
int count = 0;
while (count < 100)
{
Console.WriteLine("Downloading File {0,1} ----> {1,3}%", 1, count);
Thread.Sleep(10);
count++;
}
await Task.Delay(100);
}
}
}
Program.cs
namespace AsyncAwait
{
class Program
{
static void Main(string[] args)
{
Downloader d = new Downloader();
d.DownloadFilesAsync().Wait();
Console.WriteLine("finished");
}
}
}
In the output I see the file being downloaded
one after the other (file1
followed by file2
). I have sleep inside the while loop for both DownloadFile0
as well as DownloadFile1
.
I would expect that they happen together just like a different thread would do instead of in a strictly sequential manner. In my understanding that is what async
is all about? Is my understanding wrong? If now how do I fix this?
Upvotes: 2
Views: 136
Reputation: 27039
Let me try and explain what you are doing, and hopefully you will see why things are happening sequentially.
Imagine you, yes you, are the main thread and you start executing the main
function. You create a new Downloader
then you see the DownloadFilesAsync()
method so you go to it. You go to that method and then you see DownloadFile0()
so you go to that method. You start doing the loop, you sleep for 10 ms in each loop iteration. Once the loop is completed, you do something. This is the important part: Here you decide you do not want to do the Delay
. You ask that you want one of the threads in the threadpool to do the 100 ms of delay. Because you put the word await
, you are also saying: "Hey, ask some thread in the threadpool to wait for 100 ms and when done, let me know. Do not do anything after this. Just let me know." Then since a threadpool thread will do the waiting of 100 ms, you return immediately to the line await DownloadFile0();
. Here you also have an await
so you do not go any further. You return immediately to the main
method. Here the next thing for you to do is, since you already did d.DownloadFilesAsync()
is to do the .Wait();
. So you wait and do nothing else.
Sometime later, 100 ms later, you are interrupted: "Hey main thread, you asked one of the pool threads to wait for 100 ms. It is done. You can take it from here." So you go back to where you had left off which is: await Task.Delay(100);
in DownloadFile0()
. You proceed and there is nothing to do after this line except to return. So after this line you return to await DownloadFile0();
and since DownloadFile0()
is completely done, you move to the next line which is await DownloadFile1();
.
Therefore, DownloadFile1()
is only started after everything is done in DownloadFile0()
. Then you pretty much do the same thing in DownloadFile1()
. You, the main, thread did all of the work. The only work you asked the thread-pool threads to do is Task.Delay(100);
which is waiting 100 ms in each method. And in that time you were free, but you did nothing else but wait.
Thus all the work is sequential.
NOTE: It is possible even the Task.Delay(100)
is done sequentially. The scheduler will call the IsCompleted
method and if it is completed, it will not be done asynchronously..
If you were doing true asynchronous work like calling a web service to download a huge file, or some other IO work, while that is happening, you the main thread, instead of waiting could have been showing a nice progress bar to the user stating that you are alive and you are waiting for a file download. Or you could have been doing other work...you get the point.
Upvotes: 3
Reputation: 1907
In your DownloadFilesAsync You need to do this:
public async Task DownloadFilesAsync()
{
// In the Real World, we would actually do something...
// For this example, we're just going to print file 0, file 1.
var task1 = DownloadFile0();
var task2 = DownloadFile1();
await Task.WhenAll(task1, task2).
}
Basically, await means you wait for the execution of the task before you go to the next line. So let the tasks execute and then wait at the end with WhenAll. Also, try to read about ConfigureAwait(false), it will save you from deadlocks:)
Update Also, change Thread.Sleep to await Task.Delay(100); (to simulate file download, and use actually async function to do the file download) Thread.Sleep means your main thread will sleep as soon as it hits this line and it won't yield, so your task will go through the whole loop before it finally awaits on this line: await Task.Delay(100); and execution goes to DownloadFile1. On the other hand Task.Delay yields and execution will go to DownloadFile1 until it hits the first await.
Upvotes: 3