我不明白调用 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
中的异步方法作用域,这会导致处理 SmtpClient
和异步调用结束之间的竞争条件。而在后者中,异常被封装在返回的 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/