You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET WebForms中SmtpClient.SendMailAsync随机抛出异步操作异常

ASP.NET WebForms中QueueBackgroundWorkItem内SendMailAsync随机抛SmtpException的解决办法

问题情况

旧版ASP.NET WebForms应用,在生产环境高负载时会随机出现调用SmtpClient.SendMailAsync抛出SmtpException的情况,错误提示是:

异步操作无法启动,需在异步处理程序/模块或页面生命周期特定事件中执行

所有邮件发送逻辑都已经放在System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem的异步委托里执行,代码示例如下:

System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(async () => {
    smtpClient = new SmtpClient(smtpSection.Host, smtpSection.Port);
    smtpClient.Timeout = 10000;
    smtpClient.Credentials = new System.Net.NetworkCredential(smtpSection.Username, smtpSection.Password);
    smtpClient.EnableSsl = smtpSection.UseSsl;
    ...
    await smtpClient.SendMailAsync(mail);
});

试过加ConfigureAwait(false)、在异步函数开头设SynchronizationContext.SetSynchronizationContext(null),都没用。而且这个问题只在生产高负载时频发,调试或预发布环境基本碰不到。

问题根源

这是.NET Framework特有的同步上下文流转随机性问题:

  • 高负载下,QueueBackgroundWorkItem的异步委托执行时,ASP.NET的SynchronizationContext可能没被完全清除,导致await SendMailAsync之后意外回到了ASP.NET的同步上下文
  • SmtpClient.SendMailAsync底层有校验逻辑,一旦检测到ASP.NET同步上下文但又不在允许的页面生命周期或异步处理流程里,就会抛出那个异常
  • .NET 7及以上版本重构了同步上下文的处理逻辑,不存在这个问题

解决办法

办法1:用Task.Run包裹发送逻辑

把邮件发送的异步代码完全放进Task.Run里,强制让它在没有同步上下文的线程池线程上执行:

System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(async ct => {
    await Task.Run(async () => {
        using var smtpClient = new SmtpClient(smtpSection.Host, smtpSection.Port);
        smtpClient.Timeout = 10000;
        smtpClient.Credentials = new System.Net.NetworkCredential(smtpSection.Username, smtpSection.Password);
        smtpClient.EnableSsl = smtpSection.UseSsl;
        ...
        await smtpClient.SendMailAsync(mail, ct);
    }, ct);
});

注意:加上CancellationToken参数,配合QueueBackgroundWorkItem的取消机制,防止任务无限制运行

办法2:换用MailKit库发送邮件

SmtpClient是.NET Framework里的旧类型,已经被标记为过时。换成MailKit这个更稳定的异步邮件库,它的异步实现不受ASP.NET同步上下文的影响:

System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(async ct => {
    using var client = new MailKit.Net.Smtp.SmtpClient();
    await client.ConnectAsync(smtpSection.Host, smtpSection.Port, smtpSection.UseSsl, ct);
    await client.AuthenticateAsync(smtpSection.Username, smtpSection.Password, ct);
    await client.SendAsync(mailMessage, ct);
    await client.DisconnectAsync(true, ct);
});

办法3:升级到.NET 7及以上版本

如果业务允许的话,把应用升级到.NET 7或更高版本,从根本上解决.NET Framework的同步上下文缺陷问题。

内容的提问来源于stack exchange,提问作者Geoduck

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 19:12:44