我正在开发一个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
执行,因此您有经典的死锁场景。
最简单的解决方案是忘记SendAsync
和SendCompleted
并使用SmtpClient
的asyncawait
功能:
SmtpClient GenMailClient = new SmtpClient();
await GenMailClient.SendMailAsync(message);
// done
当然,为此,您必须在异步 fasion 中重写 asp.net mvc 操作(至少是那些发送电子邮件的操作(。如果您不想这样做,另一种解决方案是:
SmtpClient GenMailClient = new SmtpClient();
GenMailClient.Send(message);
因为您要做的是使用异步方法模拟同步发送。为什么?只需同步发送即可。