带有For循环的异步和等待

本文关键字:等待 异步 For 循环 带有 | 更新日期: 2023-09-27 18:24:40

我有一个Windows服务,它根据时间表运行各种作业。在确定要运行哪些作业后,将调度对象的列表发送到一个方法,该方法遍历列表并运行每个作业。问题是,由于外部数据库调用,一些作业可能需要长达10分钟才能运行。

我的目标是不让一个作业阻塞队列中的其他作业,基本上一次运行多个作业。我认为使用async和await可以解决这个问题,但我以前从未使用过这些。

当前代码:

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;
}

我尝试更新到这个代码:

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;
}

问题是,第一个LoadAsync在将控制权交还给循环之前等待作业完成,而不是允许所有作业启动。

我有两个问题:

  1. 高级-aysnc/wait是最佳选择,还是我应该使用不同的方法
  2. 需要更新什么以允许循环在不阻塞的情况下启动所有作业,但在所有作业完成之前不允许函数返回

带有For循环的异步和等待

高级-异步/等待是最好的选择,还是应该使用不同的方法?

async-await非常适合您尝试执行的操作,即同时卸载多个IO绑定任务。

需要更新什么以允许循环在不阻塞的情况下启动所有作业,但在所有作业完成之前不允许函数返回?

您的循环当前正在等待,因为您每次调用LoadAsync都要await。您想要的是同时执行它们,而不是等待它们全部使用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;
}

对于扇出并行异步调用,您希望启动Task来启动它们,但随后将它们作为异步未来值或promise值进行处理。您可以在所有操作完成后同步/等待它们。

最简单的方法是将for循环变成这样:

List<Task<bool>> jobs = new List<Task<bool>>();
foreach (var schedule in scheduleList)
{
    Task<bool> job = LoadAsync((int) schedule.JobId, schedule.ScheduleId); // Start each job
    jobs.Add(job);
}
bool[] finishedJobStatuses = await Task.WhenAll(jobs); // Wait for all jobs to finish running
bool allOk = Array.TrueForAll(finishedJobStatuses, p => p);

我建议使用Parallel.ForEach。它不是异步的,每次迭代都是并行运行的。正是你所需要的。

public static bool Load(IEnumerable<Schedule> scheduleList)
{
    // set the number of threads you need
    // you can also set CancellationToken in the options
    var options = new ParallelOptions { MaxDegreeOfParallelism = 5 };
    Parallel.ForEach(scheduleList, options, async schedule =>
    {
        await Load(schedule.ScheduleId);
    });
    return true;
}