Josh Josh - 1 month ago 13
C# Question

Async and Await with For Loop

I have a Windows Service that runs various jobs based on a schedule. After determining which jobs to run, a list of schedule objects is sent to a method that iterates through list and runs each job. The problem is that some jobs can take up to 10 minutes to run because of external database calls.

My goal is to not have one job block others in queue, basically have more than one run at a time. I thought that using async and await could be to solve this, but I've never used these before.

Current Code:

public static bool Load(List<Schedule> scheduleList)
{
foreach (Schedule schedule in scheduleList)
{
Load(schedule.ScheduleId);
}

return true;
}

public static bool Load(int scheduleId)
{
// make database and other external resource calls
// some jobs run for up to 10 minutes

return true;
}


I tried updating to this code:

public async static Task<bool> LoadAsync(List<Schedule> scheduleList)
{
foreach (Schedule schedule in scheduleList)
{
bool result = await LoadAsync((int)schedule.JobId, schedule.ScheduleId);
}

return true;
}

public async static Task<bool> LoadAsync(int scheduleId)
{
// make database and other external resource calls
// some jobs run for up to 10 minutes

return true;
}


The issue is that the first LoadAsync waits for the job to finish before giving control back to the loop instead of allowing all the jobs to start.

I have two questions:


  1. High Level - Are aysnc/await the best choice, or should I use a different approach?

  2. What needs to be updated to allow the loop to kick off all the jobs without blocking, but not allow the function to return until all jobs are completed?


Answer

High Level - Are async/await the best choice, or should I use a different approach?

async-await is perfect for what you're attempting to do, which is concurrently offloading multiple IO bound tasks.

What needs to be updated to allow the loop to kick off all the jobs without blocking, but not allow the function to return until all jobs are completed?

Your loop currently waits because you await each call to LoadAsync. What you want is to execute them all concurrently, than wait for all of them to finish using Task.WhenAll:

public async static Task<bool> LoadAsync(List<Schedule> scheduleList)
{
   var scheduleTaskList = scheduleList.Select(schedule => 
                          LoadAsync((int)schedule.JobId, schedule.ScheduleId)).ToList();
   await Task.WhenAll(scheduleTaskList);

   return true;
}
Comments