如何调用嵌套的异步方法

时间:2016-06-07 06:41:54

标签: c# asynchronous

我想知道什么是调用嵌套异步方法的最佳方法,如果我想要两个方法都是异步的。
首先:来电方法必须返回Task

public async Task SaveChangesAsync()
{
    await Context.SaveChangesAsync();
}
public Task UpdateAsync(List<TEntity> entities)
{
    foreach (TEntity entity in entities)
    {
        BeforeUpdate(entity);
        Context.Entry(entity).State = EntityState.Modified;
    }
    return SaveChangesAsync();
}


第二:来电方法必须有async - await,方法为

public async Task SaveChangesAsync()
{
    await Context.SaveChangesAsync();
}
public async Task UpdateAsync(List<TEntity> entities)
{
    foreach (TEntity entity in entities)
    {
        BeforeUpdate(entity);
        Context.Entry(entity).State = EntityState.Modified;
    }
    await SaveChangesAsync();
}

4 个答案:

答案 0 :(得分:4)

此代码不应使用async/await,除非这些方法中有部分内容未显示。

换句话说,这就是这些方法的编写方式:

public Task SaveChangesAsync()
{
    return Context.SaveChangesAsync();
}
public Task UpdateAsync(List<TEntity> entities)
{
    foreach (TEntity entity in entities)
    {
        BeforeUpdate(entity);
        Context.Entry(entity).State = EntityState.Modified;
    }
    return SaveChangesAsync();
}

这基本上是你没有展示的选项3,根本没有async/await的痕迹。

原因是代码的执行方式如下:

  1. 它将在调用线程上执行导致await的UpdateAsync的所有部分,这意味着foreach - 循环将在调用线程上执行
  2. 它将执行对SaveChangesAsync()的调用,直到它返回,仍在调用线程
  3. SaveChangesAsync会调用Context.SaveChangesAsync()直到它返回,仍然在调用线程
  4. 简而言之,这些方法的所有工作已经在调用线程上完成,剩下的就是 Context.SaveChangesAsync的async-ness 。如果你完全删除async/await并简单地返回将async-ness包装回来的任务,你就没有牺牲任何功能,但你已经删除了编译器为你生成的所有复杂性{{1方法。

    一般规则如下:

      

    除非您需要在 之后执行异步方法,否则请不要使用async/await

    由于您的方法在调用异步方法后立即结束,因此您最好只返回相关任务,而不是使用async/await

答案 1 :(得分:3)

await可以适用于任何Task。它不一定是async方法的结果。事实上,它根本不是任何方法的结果。

那就是说,我建议任何包含非平凡逻辑的方法都应该使用async / await

public async Task UpdateAsync(List<TEntity> entities)
{
  foreach (TEntity entity in entities)
  {
    BeforeUpdate(entity);
    Context.Entry(entity).State = EntityState.Modified;
  }
  await SaveChangesAsync();
}

这是因为async捕获了方法体中的异常,并将它们放在返回的Task上,这是调用任务返回方法时的预期行为。如果您在此处忽略async / await,那么BeforeUpdate或设置State的任何例外情况都会被直接抛出而不是放在Task上。

另一方面,您的调用方法很简单,因此您可以在那里忽略async / await

public Task SaveChangesAsync()
{
  return Context.SaveChangesAsync();
}

答案 2 :(得分:1)

当你进行异步时,需要通过调用堆栈使用awaitasync,否则代码将不会异步执行。因此,请在两种方法上使用async

  

如果异步方法不使用await运算符来标记暂停   一点,该方法作为同步方法执行,尽管如此   异步修饰符

https://msdn.microsoft.com/en-us/library/mt674882.aspx

答案 3 :(得分:1)

正确的方法是Async All the Way向下,这意味着你必须采取第二种方法! 第一种方法不能按预期工作,因为您没有与当前上下文同步而只返回task。现在您的任务将在未来的某个时间完成,但由于您没有与当前同步上下文同步,因此您无法再次获得结果(甚至是任何异常)!