SendAsync vs SendMailAsync vs new Thread vs Parallel.ForEach 用于发送 300-500 封电子邮件

SendAsync vs SendMailAsync vs new Thread vs Parallel.ForEach for sending 300-500 emails

TLDR;以下哪项(或其他)最适合向外部邮件服务器快速发送 300-500 封电子邮件?

  1. SmtpClient.SendAsync
  2. SmtpClient.SendMailAsync
  3. new Thread
  4. Parallel.ForEach

我正在修改发送电子邮件的代码,现在使用外部发件人(例如 MailGun、SendGrid 等),而不是当前与应用程序安装在同一服务器上的 HMailServer。这显然引入了延迟。

我已经阅读了上述所有内容的文档,但我很难完全理解每个内容的含义(尤其是 陷阱)。关于以上是否合适,似乎有很大的不同意见,特别是:

  1. 如果我必须等待每封邮件,那不是在模拟当前的同步方式吗?
  2. 我正在尝试使用 SmptClient 对象的单个实例,以提高速度和效率
  3. 根据今天的广泛阅读,我看到的许多示例都是旧的,可​​能没有使用最新的 .Net 功能

我欢迎实际成功实现这一目标的人提供意见。当然,我可以为每个代码编写代码,但我正在寻找有经验的人来指导我走上正确的道路。

我的简化(现有同步)代码如下:

var sb = new StringBuilder();

using (var mc = new SmtpClient() {
    Host = "127.0.0.1", // Current HMailServer installation - will be changed to external API
    DeliveryMethod = SmtpDeliveryMethod.Network,
    Port = 25,
    UseDefaultCredentials = false,
    Credentials = new NetworkCredential("Username", "Password")
})
{
    foreach(var result in GetData())
    {
        using(var mm = new MailMessage())
        {
            mm.To.Add(new MailAddress(result.Email, result.FirstName + " " + result.Surname));
            mm.Subject = "Your monthly report";
            mm.From = new MailAddress("noreply@example.com");
            mm.ReplyToList.Add(new MailAddress(result.Email));

            // Email body constructed here for each individual recipient
            mm.Body = sb.ToString();
            sb.Clear();

            mc.Send(mm);
        }
    }
}

对于发送电子邮件等 I/O-bound 任务,您 不想 使用 Parallel。如果您在 ASP.NET 上 运行,这将翻倍。此外,除非您正在进行 COM 互操作,否则您不想使用 new Thread

如果你想让它异步,最简单的方法是保持一切不变,只调用 SendAsync 而不是 Send:

var sb = new StringBuilder();

using (var mc = new SmtpClient() {
    Host = "127.0.0.1", // Current HMailServer installation - will be changed to external API
    DeliveryMethod = SmtpDeliveryMethod.Network,
    Port = 25,
    UseDefaultCredentials = false,
    Credentials = new NetworkCredential("Username", "Password")
})
{
    foreach(var result in GetData())
    {
        using(var mm = new MailMessage())
        {
            mm.To.Add(new MailAddress(result.Email, result.FirstName + " " + result.Surname));
            mm.Subject = "Your monthly report";
            mm.From = new MailAddress("noreply@example.com");
            mm.ReplyToList.Add(new MailAddress("admin@example.com"));

            // Email body constructed here for each individual recipient
            mm.Body = sb.ToString();
            sb.Clear();

            await mc.SendAsync(mm);
        }
    }
}

现在,如果您想同时执行此操作,则需要使用Task.WhenAll:

using (var mc = new SmtpClient() {
    Host = "127.0.0.1", // Current HMailServer installation - will be changed to external API
    DeliveryMethod = SmtpDeliveryMethod.Network,
    Port = 25,
    UseDefaultCredentials = false,
    Credentials = new NetworkCredential("Username", "Password")
})
{
    var tasks = GetData().Select(async result =>
    {
        using(var mm = new MailMessage())
        {
            mm.To.Add(new MailAddress(result.Email, result.FirstName + " " + result.Surname));
            mm.Subject = "Your monthly report";
            mm.From = new MailAddress("noreply@example.com");
            mm.ReplyToList.Add(new MailAddress("admin@example.com"));

            var sb = new StringBuilder();
            // Email body constructed here for each individual recipient
            mm.Body = sb.ToString();

            await mc.SendAsync(mm);
        }
    });
    await Task.WhenAll(tasks);
}

(注意 StringBuilder 不再共享)

我还没有大规模使用 SendGrid SMTP API,但我已经用相当多的请求访问了他们的 REST API。