I thought I understand the async-await
pattern in C#
but today I\'ve found out I really do not.
In a simple code snippet like this. I have
An await
is an asynchronous wait. It is not a blocking call and allows the caller of your method to continue. The remainder of the code inside the method after an await
will be executed when the Task
returned has completed.
In the first version of your code, you allow callers to continue. However, each iteration of the loop will wait until the Task
returned by GetStringAsync
has completed. This has the effect of sequentially downloading each URL, rather than concurrently.
Note that the second version of your code is not asynchronous insofar as it uses threads to perform the work in parallel.
If it were asynchronous, it would retrieve the webpage content using only one thread but still concurrently.
Something like this (untested):
public static async Task Test()
{
int ret = 0;
HttpClient client = new HttpClient();
List taskList = new List();
for (int i = 1000; i <= 1100; i++)
{
var i1 = i;
taskList.Add(client.GetStringAsync($"https://en.wikipedia.org/wiki/{i1}"));
}
await Task.WhenAll(taskList.ToArray());
return ret;
}
Here, we start the tasks asynchronously and add them to the taskList
. These tasks are non-blocking and will complete when the download has finished and the string retrieved. Pay attention to the call to Task.WhenAll
rather than Task.WaitAll
: the former is asynchronous and non-blocking, the latter is synchronous and blocking. This means that, at the await
, the caller of this Test()
method will receive the Task
returned: but the task will be incomplete until all of the strings are downloaded.
This is what forces async
/await
to proliferate throughout the stack. Once the very bottom call is asynchronous, it only makes sense if the rest of the callers all the way up are also asynchronous. Otherwise, you are forced to create a thread via Task.Run()
calls or somesuch.