【问题标题】:Async-await overuse [duplicate]异步等待过度使用[重复]
【发布时间】:2021-06-30 19:11:34
【问题描述】:

我有一个关于过度使用 async-await 运算符的问题。
我有一个Zoo 实例列表,我需要将每个Zoo 的每个成员分别插入到数据库中。这是Zoo 类定义:

class Zoo
{
public Dog Dog { get; set; }
public Cat Cat { get; set; }
public Bear Bear { get; set; }
}

下面提供了插入Zoo的方法。 Zoo 的每个实例及其成员都应该在插入之前进行验证或转换,因此我使用 Parallel.For 处理它们:

public void Save(List<Zoo> zoos)
{
 Parallel.For(0, zoos.Count, new ParallelOptions { MaxDegreeOfParallelism = 4 }, async i =>
  {
  ...
  Task saveDog = Task.Run(async () => await InsertDogAsync(zoos[i], string connectionString));
  Task saveCat = Task.Run(async () => await InsertCatAsync(zoos[i], string connectionString));
  Task saveBear = Task.Run(async () => await InsertBearAsync(zoos[i], string connectionString));
  await Task.WhenAll(new Task[] { saveDog, saveCat, saveBear });
  ...
  });
}

我使用async 方法将动物插入数据库。例如这里是Dog:

private static async Task InsertDogAsync(Zoo zoo, string connectionString)
{
 using SqlConnection connection = new SqlConnection(connectionString);
 await connection.OpenAsync();
 ...
 using SqlCommand insertCommand = new SqlCommand("sp_insertDog", connection); // stored procedure
 ...
 await insertCommand.ExecuteNonQueryAsync();
}

我的问题如下:是否过度使用 async-await 运算符?正如我所意识到的,每个await 运算符在完成之前都会释放线程,但是在并行任务中调用方法,因此线程用于不同的任务。也许从Task.Run lambda 中删除一些async-await 更容易接受?

【问题讨论】:

  • 如果任务是 IO 绑定的,我假设它们是,那么 Task.Run 并不是真正需要的。我也怀疑你真的需要做Parallel.For。真正的问题不是过度使用 async/await,而是在使用中,因为你应该从上到下使用它,所以 Save 方法应该是 async 并返回一个 Task 并且任何调用它都应该是 async一直到顶部。此外,InsertDogAsync 实际上应该是异步的并且还返回一个 Task。事实上,代码不会编译,因为您不能在不是 async 的方法中包含 await
  • 可能是我的 C# 离我有点远,但是......为什么你觉得你必须在 Task.Run 调用中包装任务?为什么不去Task saveDog = InsertDogAsync(zoos[i], connectionString);
  • @juharr 对不起,我在InsertDogAsync 错过了async Task,当然它会返回Task
  • 几件事:看起来您正在尝试进行过早优化。别担心我们都去过那里。 99% 的时间你不需要像那样并行地做任何事情。它不会让它更快,尤其是在数据库方面。 2. 您应该尝试使用 EntityFramework Core。一开始设置很痛苦,但你会非常喜欢它。 3. 我并没有真正注意到过多使用 async-await 对性能造成的影响,但我(几乎)从不执行 Task.Run,​​除非我必须这样做。无论如何,EF Core 都会为您处理所有这些事情
  • 我会强烈建议批量插入,这将首先排除并行化的必要性,并且要快得多

标签: c# .net sql-server async-await parallel.for


【解决方案1】:

这不是await 过度使用的情况,这是await 滥用的情况。 Parallel.ForEach 方法 is not async-friendly,并用 async 委托提供给它是一个错误:它没有做你期望它做的事情。如果要同时启动多个异步操作,同时控制并发级别,正确​​使用的 API 是 Parallel.ForEachAsync 方法。此方法将在即将推出的 .NET 6 中引入,因此目前您只能使用自制解决方案,例如在 this 问题中找到的解决方案。

【讨论】:

  • 哦,谢谢……对我来说,这似乎比我预期的要复杂得多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-12
  • 2021-12-30
  • 1970-01-01
  • 2018-12-18
  • 1970-01-01
  • 2019-06-29
  • 2018-06-25
相关资源
最近更新 更多