【问题标题】:Sending Email asynchronously in ASP.NET C#在 ASP.NET C# 中异步发送电子邮件
【发布时间】:2014-11-22 23:17:18
【问题描述】:

我想知道是否有一种方法可以在 asp.net c# 中异步发送电子邮件。我使用以下代码发送电子邮件:

 if (checkObavijest.Checked)
            {
                List<hsp_Kupci_Newsletter_Result> lista = ServisnaKlasa.KupciNewsletter();
                for (int i = 0; i < lista.Count; i++)
                {
                    MailMessage mail = new MailMessage();

                    mail.From = new MailAddress("*******");
                    mail.To.Add(lista[i].Email);
                    mail.Subject = "Subject";
                    mail.SubjectEncoding = Encoding.UTF8;
                    mail.BodyEncoding = Encoding.UTF8;
                    mail.IsBodyHtml = true;
                    mail.Priority = MailPriority.High;
                    mail.Body = "Some message";
                    SmtpClient smtpClient = new SmtpClient();
                    Object state = mail;
                    smtpClient.Credentials = new NetworkCredential("****@gmail.com", "****");
                    smtpClient.Port = 587;
                    smtpClient.Host = "smtp.gmail.com";
                    smtpClient.EnableSsl = true;
                    smtpClient.SendCompleted += new SendCompletedEventHandler(smtpClient_SendCompleted);
                    try
                    {
                        smtpClient.SendAsync(mail, state);
                    }
                    catch (Exception ex)
                    {

                    }
                }

 void smtpClient_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {

            MailMessage mail = e.UserState as MailMessage;

            if (!e.Cancelled && e.Error != null)
            {
                string poruka = "Emailovi poslani!";
                ClientScript.RegisterStartupScript(this.GetType(), "myalert", "alert('" + poruka + "');", true);
            }
        }

但是当我勾选发送电子邮件的选项时,页面加载大约 40 秒,然后在 40 秒后发送电子邮件。如您所见,我正在从我的数据库中提取 20-30 封电子邮件。我认为这是异步发送邮件的正确方法,但显然不是......如何将电子邮件发送到另一个线程,这样它就不会影响正在使用此页面的用户?有人可以帮我解决这个问题吗?谢谢!

【问题讨论】:

    标签: c# asp.net email asynchronous


    【解决方案1】:

    原因在MSDN documentation on SmtpClient.SendAsync这里提到

    调用 SendAsync 后,必须等待电子邮件传输到 在尝试使用发送发送另一封电子邮件之前完成 或 SendAsync。

    您可以使用 ThreadPool 通过包装的方法调用来调用 SendAsync。这会给你一个类似“即发即弃”的场景。

    类似这样的:

    public void SendViaThread(System.Net.Mail.MailMessage message) {
        try {
            System.Threading.ThreadPool.QueueUserWorkItem(SendViaAsync, message);
        } catch (Exception ex) {
            throw;
        }
    }
    
    private void SendViaAsync(object stateObject) {
        System.Net.Mail.SmtpClient smtp = new System.Net.Mail.SmtpClient();
        System.Net.Mail.MmailMessage message = (MmailMessage)stateObject;
        ...
        smtp.Credentials = new NetworkCredential("...");
        smtp.Port = 587;
        smtp.Host = "...";
        ...
        smtp.SendCompleted += new SendCompletedEventHandler(smtpClient_SendCompleted);
        ...
        smtp.Send(message);
    }
    

    你会在你的代码中调用SendViaThread(mail)。所以,你的循环现在变成了:

    for (int i = 0; i < lista.Count; i++) {
        MailMessage mail = new MailMessage();
        mail.From = new MailAddress("...");
        mail.To.Add(lista[i].Email);
        mail.Subject = "...";
        ...
        mail.Body = "...";
        SendViaThread(mail);
     }
    

    【讨论】:

    • 我试试看,为什么你把 sendviathread 函数声明为 bool,为什么不 void?
    • 哦..这是我的一个项目中的代码的一部分。我正在做更多的工作并返回一个布尔值来指示问题。你真的不需要那个。感谢您指出,我将删除该部分。
    • 您能否更具体地说明如何将其构建到我现有的代码中?我不确定我应该在这里做什么......
    • @perkes456:我已经让答案更清楚了。您要做的就是删除现有循环中的smtpclient 相关代码,只需调用SendViaThread(mail) 而不是smtpClient.SendAsync(mail, state)
    • 我应该这样做:对象状态=邮件;尝试 { SendViaAsync(state); SendViaThread(邮件); } 或者只是尝试 { SendViaThread(mail); } 哪一个?
    【解决方案2】:

    如果您不想让主线程等待,请尝试将其放入任务中。

    Task.Factory.StartNew( () => {
      // do mail stuff here
    });
    

    请注意,尽管每次生成任务时,它都会生成一个新的(或重复使用的)系统已创建的线程。如果您要发送 30 封电子邮件,您就有可能触发 lot 的线程(系统也有一个可编程的上限)。 Task.Factory.StartNew 是一种非常简单的方法来执行即发即弃的过程。

    http://msdn.microsoft.com/en-us/library/dd321439%28v=vs.110%29.aspx (在此示例中,代码还保留了 Tasks 的集合,如果您愿意管理它们,这很好 - 但实际上,如果您想一劳永逸,则只需要 Task.Factory.StartNew(()=&gt;{ 位。请小心,因为您不希望有孤立线程与您的内存使用量挂钩。

    【讨论】:

    • 有什么办法可以在这些线程完成发送邮件后处理掉它们...
    • 并非如此,您可以让系统处理释放这些任务。一旦他们完成,他们就完成了。 -- 看到这个:blogs.msdn.com/b/pfxteam/archive/2012/03/25/10287435.aspx 顺便说一句,你也可以使用 Task.Run(而不是 .StartNew)——也可以看看。如果您对跟踪您的任务保持冷静,您可以将它们添加到示例中列出的集合中 - 或者在启动时将它们包装在using 中(使用清理自身及其本地人)。但我建议让 .NET 处理清理工作,不要担心。
    • 过去,我将衍生线程添加到集合中,以便我可以关注它们的进程、它们在做什么,并在需要时将它们删除(多线程是有趣的[不是]世界倒下)。 Task.Run/StartNew 让您轻松搞定,而不必过多担心线程状态、竞争条件等。这里非常适合您的即发即弃场景。
    • 使用我在问题中发布的第一个代码时出现“内存不足”错误,这可能是错误的原因吗?
    猜你喜欢
    • 2011-03-25
    • 2011-04-03
    • 1970-01-01
    • 1970-01-01
    • 2012-07-27
    • 2015-03-07
    • 1970-01-01
    • 1970-01-01
    • 2011-05-06
    相关资源
    最近更新 更多