C# 为什么我们会定期得到;异步调用已在进行中”;调用SmtpClient.Send时?

C# 为什么我们会定期得到;异步调用已在进行中”;调用SmtpClient.Send时?,c#,.net,smtpclient,C#,.net,Smtpclient,我们有一些(同步)电子邮件代码,它创建了一个类来创建SmtpClient,然后发送一封电子邮件。SmtpClient不可重复使用;但是,我们不时会遇到以下例外情况: System.Web.HttpUnhandledException (0x80004005): Exception of type 'System.Web.HttpUnhandledException' was thrown. ---> System.InvalidOperationException: An asynchro

我们有一些(同步)电子邮件代码,它创建了一个类来创建SmtpClient,然后发送一封电子邮件。SmtpClient不可重复使用;但是,我们不时会遇到以下例外情况:

System.Web.HttpUnhandledException (0x80004005): Exception of type 'System.Web.HttpUnhandledException' was thrown. ---> System.InvalidOperationException: An asynchronous call is already in progress. It must be completed or canceled before you can call this method.
   at System.Net.Mail.SmtpClient.Send(MailMessage message)
   at EmailSender.SendMail(MailAddress fromMailAddress, string to, String subject, String body, Boolean highPriority) in ...\EmailSender.cs:line 143
代码如下所示:

// ...
var emailSender = new EmailSender();
emailSender.SendMail(toEmail, subject, body, true);
// emailSender not used past this point
// ...

public class EmailSender : IEmailSender
{
    private readonly SmtpClient smtp;

    public EmailSender()
    {
        smtp = new SmtpClient();
    }

    public void SendMail(MailAddress fromMailAddress, string to, string subject, string body, bool highPriority)
    {
        if (fromMailAddress == null)
            throw new Exception();
        if (to == null)
            throw new ArgumentException("No valid recipients were supplied.", "to");

        // Mail initialization
        var mailMsg = new MailMessage
        {
            From = fromMailAddress,
            Subject = subject,
            Body = body,
            IsBodyHtml = true,
            Priority = (highPriority) ? MailPriority.High : MailPriority.Normal
        };

        mailMsg.To.Add(to);


        smtp.Send(mailMsg);
    }
}

我的猜测是
SmtpClient
的设计不是为了同时发送多条消息

我会改成这样:

public class EmailSender : IEmailSender
{
    Queue<MailMessage> _messages = new Queue<MailMessage>();
    SmtpClient _client = new SmtpClient();

    public EmailSender()
    {
    }

    public void SendMail(MailAddress fromMailAddress, string to, string subject, string body, bool highPriority)
    {
        if (fromMailAddress == null)
            throw new ArgumentNullException("fromMailAddress");
        if (to == null)
            throw new ArgumentException("No valid recipients were supplied.", "to");

        // Mail initialization
        var mailMsg = new MailMessage
        {
            From = fromMailAddress,
            Subject = subject,
            Body = body,
            IsBodyHtml = true,
            Priority = (highPriority) ? MailPriority.High : MailPriority.Normal
        };

        mailMsg.To.Add(to);

        lock (_messages)
        {
            _messages.Enqueue(mailMsg);
            if (_messages.Count == 1)
            {
                ThreadPool.QueueUserWorkItem(SendEmailInternal);
            }
        }
    }

    protected virtual void SendEmailInternal(object state)
    {
        while (true)
        {
            MailMessage msg;
            lock (_messages)
            {
                if (_messages.Count == 0)
                    return;
                msg = _messages.Dequeue();
            }

            _client.Send(msg)
        }
    }
}
公共类EmailSender:IEmailSender
{
队列_消息=新队列();
SmtpClient _client=新的SmtpClient();
公共电子邮件发送者()
{
}
public void SendMail(MailAddress fromMailAddress,string to,string subject,string body,bool highPriority)
{
if(fromMailAddress==null)
抛出新ArgumentNullException(“fromMailAddress”);
如果(to==null)
抛出新的ArgumentException(“未提供有效的收件人。”,“收件人”);
//邮件初始化
var mailMsg=新邮件消息
{
From=fromMailAddress,
主语,
身体,
IsBodyHtml=true,
优先级=(高优先级)?MailPriority.High:MailPriority.Normal
};
mailMsg.To.Add(To);
锁定(_消息)
{
_messages.Enqueue(mailMsg);
如果(_messages.Count==1)
{
ThreadPool.QueueUserWorkItem(SendEmailInternal);
}
}
}
受保护的虚拟void SendEmailInternal(对象状态)
{
while(true)
{
邮件消息;
锁定(_消息)
{
如果(_messages.Count==0)
返回;
msg=_messages.Dequeue();
}
_client.Send(msg)
}
}
}
因为实际上没有理由在构造函数中创建客户机

我还进行了更改,以便类在
fromMailAddress
为null时抛出
ArgumentNullException
而不是
Exception
。一个空的
异常
说明不了什么

更新


代码现在使用线程池线程进行发送(并重用smtpclient)

您需要使用
dispose
使用
或通过实现类EmailSender的一次性模式来处理(这里更合适,因为您将SmtpClient的生存期绑定到构造函数中EmailSender的生存期。)


这可能会解决此异常。

或者OP可以锁定该对象,而不是创建新的SmtpClient实例,直到消息发送。是的,有一个很好的理由在构造函数中创建客户端:当您向同一服务器发送多封电子邮件时,连接将被池化。请参阅“我不明白为什么我们需要锁”的备注部分。我们不共享这两个类的实例。创建EmailSender类;它的sendmail方法被调用,然后完成。没有任何东西是静态的。您没有对不共享表示担忧,因为这是错误消息上显示的情况。我的问题是“SmtpClient未被重用”,并且在代码示例中有一条注释“//emailSender未被使用过这一点”:(我同意我们应该处理,但它真的会导致此问题吗?可能是这样的。“偶尔”根据您的问题,可能与超时、清理等有关。添加正确的资源管理,并查看异常是否仍会出现。如果无法重现,则很难确定问题。