Search code examples
c#asp.net.netazure-service-fabric

Adding items to a list from asynchronous method


I need to return items from partitions in service fabric and add them to a list. The results come from async methods. I try to understand what is happening to get it to run faster. Does the loop wait until await is returned for each GetItems, or does the loop continue and start a new GetItems for the next partition?

List<string> mylist = new List<string>();

foreach(var partition in partitions)
{
   var int64RangePartitionInformation = p.PartitionInformation as Int64RangePartitionInformation;
   if (int64RangePartitionInformation == null) continue;
   var minKey = int64RangePartitionInformation.LowKey;
   var assetclient = ServiceProxy.Create<IService>(serviceName, new ServicePartitionKey(minKey));
   var assets = await assetclient.GetItems(CancellationToken.None);
   mylist.AddRange(items)
}

Solution

  • The await keyword tells the compiler to refactor your method to a state machine. Once your async method is called, everything before the first await will be executed. The rest of the tasks will be registered for a delayed execution, and depending of the current configuration can be executed immediately and synchronously or on another thread.

    If the awaited task is executed asynchronously, the actual method call returns, so the thread is free to do anything else, eg. refresh the UI. This refactored state machine-like method is then called again and again, polls whether the awaited task is finished. Once it is finished, the state is switched so the codes after the awaited line will be executed and so on.

    So logically yes, the loop waits until the results are there, but in reality the thread is not blocked because of the state machine-like behavior mentioned above.

    See a detailed explanation here.

    Update:

    If the results can be obtained parallelly from all partitions, do not await them one by one. Instead, use Parallel.ForEach or just populate a Task collection in your foreach loop, and finally await them in one step:

    await Task.WhenAll(myTasks);