1

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

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

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

这是主要代码:

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();

这是 SendCompleted 回调:

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

1 回答 1

1

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

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

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

这为SendCompleted回调提供了执行的机会,因为相应的线程被阻塞了,它被阻塞等待SendComplete执行,所以你有典型的死锁场景。

最简单的解决方案是忘记SendAsyncSendCompleted使用以下async\await功能SmtpClient

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

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

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

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

于 2018-05-28T14:50:19.267 回答