我不明白这两种调用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/

10-11 23:54