SmtpClient SendAsync 从不调用 SendDone 回调



我正在开发一个MVC C#网站.NET 4.0,在一个控制器中,我需要发送大量邮件。 我从一个独立程序中复制了自己的代码,在该程序中我使用 SendAsynch 传递回调。 在斯坦单独应用程序中一切正常。

当然,在控制器中,相同的代码不起作用:永远不会调用回调。

有人可以告诉我为什么相同的代码可以独立工作而不是在控制器中工作?

这是主要代码:

SmtpClient GenMailClient = new SmtpClient();
GenMailClient.SendCompleted += SendCompleted;
GenMailClient.SendAsync(message, ArgumentForTheCallBack);
WaitingMails++;
var startTime = DateTime.Now;
//waits until the SendCompleted is called (FOREVER!)
while (WaitingMails != 0)
Thread.Sleep(500);    
GenMailClient.Dispose();

这是发送完成的回调:

private void SendCompleted(object sender, AsyncCompletedEventArgs e)
{
WaitingMails--;
}

这是因为SmtpClient.SendAsync捕获当前SynchronizationContext并在捕获的上下文(如果有(上执行回调(SendCompleted(。

在 mvc(非核心(asp.net - 每个请求都有相应的同步上下文。通过以下方式阻止与该上下文对应的线程

while (WaitingMails != 0)
Thread.Sleep(500);

这不会SendCompleted回调提供执行的机会,因为相应的线程被阻塞,并且它被阻止等待SendComplete执行,因此您有经典的死锁场景。

最简单的解决方案是忘记SendAsyncSendCompleted并使用SmtpClientasyncawait功能:

SmtpClient GenMailClient = new SmtpClient();
await GenMailClient.SendMailAsync(message);
// done

当然,为此,您必须在异步 fasion 中重写 asp.net mvc 操作(至少是那些发送电子邮件的操作(。如果您不想这样做,另一种解决方案是:

SmtpClient GenMailClient = new SmtpClient();
GenMailClient.Send(message);

因为您要做的是使用异步方法模拟同步发送。为什么?只需同步发送即可。

最新更新