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

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

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 RegisterUser(RegisterViewModel model) { // ... await Mailer.SendAsync(user.Email, subject, body); return View(model); } 

有两个实际差异:

  1. 第二个选项不会创建允许async-await使用的状态机机制。 这将对性能产生轻微的积极影响。
  2. exception处理会有所不同。 将方法标记为async任何exception都存储在返回的任务中(来自异步部分和同步部分),并且仅在等待(或等待)任务时抛出。 当它不是async ,同步部分的exception就像任何其他方法一样。

我的建议 :使用第二个来提高性能,但要注意exception和错误。


显示差异的示例:

 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 

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

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

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

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

如果您要将其更改为:

 Task Get() { using (something) { return GetX(); } } 

然后, using块不再保护封装在x中的执行,并且something内容将比第一种情况更早地处理。 例如, something是entity framework上下文时很重要。

try块中return await也是如此。

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

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

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