我不明白这两种调用SendMailAsync的实现之间的区别。在大多数情况下,使用第一种方法时,我会收到TaskCanceledException,但是使用第二种方法时,一切都会按预期进行。
我以为这两种消耗方法是等效的,但显然我遗漏了一些东西。
这似乎与TaskCanceledException when not awaiting有关,但与之相反:
// Common SmtpEmailGateway library
public class SmtpEmailGateway : IEmailGateway
{
public Task SendEmailAsync(MailMessage mailMessage)
{
using (var smtpClient = new SmtpClient())
{
return smtpClient.SendMailAsync(mailMessage);
}
}
}
// Caller #1 code - Often throws TaskCanceledException
public async Task Caller1()
{
// setup code here
var smtpEmailGateway = new SmtpEmailGateway();
await smtpEmailGateway.SendEmailAsync(myMailMessage);
}
// Caller #2 code - No problems
public Task Caller2()
{
// setup code here
var smtpEmailGateway = new SmtpEmailGateway();
return smtpEmailGateway.SendEmailAsync(myMailMessage);
}
编辑:事实证明Caller2方法也引起异常,由于被调用的WebJobs框架,我只是没有看到它们。 Yuval的解释清除了所有这些问题,并且是正确的。
最佳答案
两种方法调用之间的区别在于,前者在被调用时会异步使用await
等待。因此,TaskCanceledException
从内部SendEmailAsync
调用传播,这是由于您没有等待using
范围内的async方法而造成的,这导致了SmtpClient
的处置与异步调用结束之间的竞争。在后者中,异常被封装在return Task
对象内,我不确定您是否在等待。这就是为什么在前一种情况下,您会立即看到异常。
首先要做的是正确等待网关内的SendEmailAsync
:
public class SmtpEmailGateway : IEmailGateway
{
public async Task SendEmailAsync(MailMessage mailMessage)
{
using (var smtpClient = new SmtpClient())
{
return await smtpClient.SendMailAsync(mailMessage);
}
}
}
然后,您可以使用第二种方法来避免创建状态机的开销。现在的区别是,您可以保证
SmtpClient
仅在异步操作完成后才能处理。关于c# - SendMailAsync导致TaskCanceledException的原因?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33523922/