SmtpClient SendAsync从不调用SendCompleted回调

时间:2018-05-28 14:31:45

标签: c# asp.net-mvc callback sendasync

我正在开发一个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--;
}

1 个答案:

答案 0 :(得分:1)

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

在asp.net mvc(非核心)中 - 每个请求都有相应的同步上下文。您可以通过

来阻止与该上下文对应的线程
while (WaitingMails != 0)
    Thread.Sleep(500);

这不会导致SendCompleted回调执行,因为相应的线程被阻止,并且它被阻止等待SendComplete执行,所以你有经典的死锁场景。

最简单的解决方法是忘记SendAsyncSendCompleted并使用async\await的{​​{1}}功能:

SmtpClient

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

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

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