【问题标题】:Chaining sequential async tasks within a ForEach loop: Is this a good way to do it?在 ForEach 循环中链接顺序异步任务:这是一个好方法吗?
【发布时间】:2020-05-22 08:41:04
【问题描述】:

我正在开发 Windows 服务,并且我有两个相互依赖的相关调用,我想为每个“对”或“一组”调用异步运行。有几种方法可以做到这一点,我尝试了几种不同的方法并解决了这个问题,因为使用一个代码块来处理比两个单独的代码块更方便,它们有自己的 await Task.WhenAll() 调用。在我的测试中,这似乎按预期工作,但我以前从未像这样将两个任务链接在一起,我想知道这是否是一种好方法,是否有更合适的方法来获得相同的结果(单个代码块)。

这就是我所拥有的。这看起来是一种合理的任务链方式吗?如果不是,请告诉我原因。

提前致谢。 -弗兰克

//get all pending batches
foreach (string batchId in workload)
{
    try
    {
        // I am using this ContinueWith pattern here to aggregate the Tasks
        // which makes it more convenient to handle exceptions in one place
        Task t = bll.GetIncomingBatchAsync(batchId).ContinueWith(
            task => bll.SaveIncomingBatchAsync(task.Result),
            TaskContinuationOptions.OnlyOnRanToCompletion);

        saveBatchTasks.Add(t);
    }
    catch (Exception ex)
    {
        _logger.WriteError(ex, "ProcessWorkloadAsync error building saveBatchTasks!");
        throw ex;
    }
}

try
{
    await Task.WhenAll(saveBatchTasks);
}
catch (Exception ex)
{
    _logger.WriteError(ex, "ProcessWorkloadAsync error executing saveBatchTasks!");
    throw ex;
}

【问题讨论】:

  • 我希望bll对象是线程安全的,因为它会被多个线程并发访问而无需同步。
  • 它应该是线程安全的,因为传入的变量是 by_val 并且其他一切都是类方法的本地。使用 bll 类本身的操作在它自己的托管线程中运行,并且每个调用都获得自己的 bll 实例,该实例包含在 using 语句中。谢谢你让我考虑一下! :)

标签: c# asynchronous async-await task-parallel-library continuewith


【解决方案1】:

不,you should not use ContinueWith。请改用await

如果您担心跨两个函数分离逻辑,只需使用本地函数:

//get all pending batches
foreach (string batchId in workload)
{
  try
  {
    async Task GetAndSave()
    {
      var result = await bll.GetIncomingBatchAsync(batchId);
      await bll.SaveIncomingBatchAsync(result);
    }

    saveBatchTasks.Add(GetAndSave());
  }
  catch (Exception ex)
  {
    _logger.WriteError(ex, "ProcessWorkloadAsync error building saveBatchTasks!");
    throw ex;
  }
}

【讨论】:

  • 谢谢斯蒂芬,我希望你能加入。:)
【解决方案2】:

一般不推荐将老式的ContinueWith 方法与 async/await 结合使用,因为后者是为了取代前者而发明的。如果需要,您可以使用LINQ 在一行中创建任务:

Task[] saveBatchTasks = workload.Select(async batchId =>
{
    var result = await bll.GetIncomingBatchAsync(batchId);
    await bll.SaveIncomingBatchAsync(result);
}).ToArray();

await Task.WhenAll(saveBatchTasks);

【讨论】:

  • 谢谢西奥多。睡在上面之后,我得出了同样的结论,这正是我在提出 ContinueWith 概念之前所做的事情。
猜你喜欢
  • 1970-01-01
  • 2011-11-21
  • 2017-07-13
  • 2023-03-11
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 2013-07-25
  • 1970-01-01
相关资源
最近更新 更多