【问题标题】:Async and Await with For Loop [duplicate]使用 For 循环进行异步和等待 [重复]
【发布时间】:2014-11-22 01:13:12
【问题描述】:

我有一个基于计划运行各种作业的 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/await 是最佳选择,还是应该使用其他方法?
  2. 需要更新哪些内容以允许循环在不阻塞的情况下启动所有作业,但在所有作业完成之前不允许函数返回?

【问题讨论】:

    标签: c# .net multithreading asynchronous async-await


    【解决方案1】:

    高级 - async/await 是最佳选择,还是我应该使用不同的方法?

    async-await 非常适合您尝试做的事情,即同时卸载多个 IO 绑定任务。

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

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

    【讨论】:

    • 看起来是个不错的解决方案。当我从非异步方法调用 LoadAsync 时,如果我将该调用包装在 try/catch 块中,是否会从运行的每个单独作业中捕获异常?
    • @Josh 如果您从非异步方法调用,则在等待WhenAll 的调用之前,您不会收到异常。
    • 与@IUnknown 使用Parallel.ForEach 的方法相比,您的方法有什么优势吗?
    • 我的方法除了你在await Task.WhenAll 上执行的线程之外不使用额外的线程。另外,Parallel.ForEach and async-await dont play along nicely。你的 lambda 最终会被翻译成 async void,这是应该避免的。
    • 你好,我需要你的帮助,我是 asyn 和 await 的新手,我在 for 循环中使用过,但它的正确与否,请你帮帮我
    【解决方案2】:

    我建议使用 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;
    }
    

    【讨论】:

    • 这会产生一个编译错误——“不能等待一个布尔值”,所以我仍然需要调用一个异步版本的 Load 来返回 Task 而不是 bool。这种方法的主要优点是我不需要使包含 Parallel.ForEach 异步的调用方法吗?我假设错误处理与此相同或使用异步调用者。
    • 这种方法的问题是它不会等到所有作业都完成后才退出。使用 await Task.WhenAll 强制函数等待。
    【解决方案3】:

    对于扇出并行异步调用,您希望触发任务以启动它们运行,然后将它们作为异步未来或承诺值处理。全部完成后,您可以在最后同步/等待它们。

    最简单的方法是将你的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);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-15
      • 2021-10-18
      • 1970-01-01
      • 1970-01-01
      • 2021-09-09
      • 1970-01-01
      • 1970-01-01
      • 2018-06-17
      相关资源
      最近更新 更多