在异步方法中返回和等待任务之间的区别

时间:2014-06-08 19:55:01

标签: c# .net task-parallel-library async-await

以下方法之间有什么区别吗?一个比另一个好吗?

public static async Task SendAsync1(string to, string subject, string htmlBody) {
  // ...
  await smtp.SendMailAsync(message);
  // No return statement
}

public static Task SendAsync2(string to, string subject, string htmlBody) {
  // ...
  return smtp.SendMailAsync(message);
}

将从MVC控制器方法调用此方法;例如:

public async Task<ActionResult> RegisterUser(RegisterViewModel model)
{
  // ...
  await Mailer.SendAsync(user.Email, subject, body);
  return View(model);
}

3 个答案:

答案 0 :(得分:7)

有两个实际差异:

  1. 第二个选项不会创建允许async-await使用的状态机机制。这将对性能产生次要正面影响。
  2. 异常处理会有所不同。将方法标记为async时,任何异常都存储在返回的任务中(来自异步部分和同步部分),并且仅在等待(或等待)任务时抛出。当它不是async时,同步部分的异常就像任何其他方法一样。
  3. 我的建议:使用第二个来提高性能,但请留意异常和错误。


    显示差异的示例:

    public static async Task Test()
    {
        Task pending = Task.FromResult(true);
        try
        {
            pending = SendAsync1();
        }
        catch (Exception)
        {
            Console.WriteLine("1-sync");
        }
    
        try
        {
            await pending;
        }
        catch (Exception)
        {
            Console.WriteLine("1-async");
        }
    
        pending = Task.FromResult(true);
        try
        {
            pending = SendAsync2();
        }
        catch (Exception)
        {
            Console.WriteLine("2-sync");
        }
    
        try
        {
            await pending;
        }
        catch (Exception)
        {
            Console.WriteLine("2-async");
        }
    }
    
    public static async Task SendAsync1()
    {
        throw new Exception("Sync Exception");
        await Task.Delay(10);
    }
    
    public static Task SendAsync2()
    {
        throw new Exception("Sync Exception");
        return Task.Delay(10);
    }
    

    <强>输出:

    1-async
    2-sync
    

答案 1 :(得分:5)

await的第一个方法将导致编译器创建状态机,因为一旦the await关键字被命中,该方法将返回给调用者,一旦等待的部分完成,它具有从它停止的地方恢复。

但是,由于等待后你没有做任何事情(没有延续),所以不需要那个状态机。

在这种情况下,第二种方法是首选,您可以省略async关键字,因为您没有等待任何事情

答案 2 :(得分:5)

首先,我认为您的示例中的代码不会编译。您需要从SendAsync2中删除“async”关键字。

如果你这样做,那么这些方法可以互换使用,所以不,这种情况没有区别。我更喜欢没有异步/等待的那个。

然而,有些情况似乎没有区别,但区别在于细节。例如,考虑以下代码:

async Task<X> Get() 
{
     using (something) 
     {
          return await GetX();
     }
}

如果您要将其更改为:

Task<X> Get() 
{
    using (something)
    {
        return GetX(); 
    }
}

然后using块不再保护封装在x中的执行,并且something将比第一种情况更早地处置。例如,当something是实体框架上下文时,这很重要。

return await块中的try也是如此。