如何使用多个线程发送MailMessages?

Pan*_*mar 3 c# multithreading windows-services mailmessage .net-3.5

我希望你们会因为线程完全缺乏方向而忍受我.我必须实现一个邮件队列处理系统,我必须通过Windows服务发送在数据库中排队的电子邮件.

它不是生产者 - 消费者模式.我一次把10行说成数据.该数据表包含序列化的MailMessage对象和SMTP发送详细信息.如果我必须使用说固定数量的线程(比如6个线程)现在我将如何从线程中的数据表中获取一行然后发送邮件并再次返回以查看是否还有剩余的行?

实现这一点的任何简单逻辑都可以,最好用C#中的简单示例.

我使用的是.NET 3.5

Shi*_*mar 6

由于发送电子邮件是一个I/O绑定进程,因此生成线程以发送电子邮件将无法实现太多(如果有的话)加速.

如果您正在使用属于Windows的SMTP服务器,那么当您"发送"电子邮件时,它实际上并未在该时刻发送.它位于服务器上的队列中,服务器尽可能快地发送它们.发送电子邮件实际上是一个缓慢的过程.

我想我说的是有两种选择:

  1. 只需按顺序发送它们,看看它是否符合您的性能要求.
  2. 你可以使用一个名为"数据并行"的并行编程概念我在博客文章中用数据并行 - 在C#/ .NET中进行并行编程实例

基本上,您正在做的是,您将获得所有数据(一次性完成).原因是批量获取数据也会减慢你的进程,所以如果你对性能感兴趣(这就是我猜你试图使用线程的原因),那么就不要多次前往数据库服务器(这也是I/O绑定在两个级别,网络I/O以及磁盘I/O).

因此,获取您的数据,并将其拆分为块或分区.这一点在我指出的文章中都有解释.天真的实现将是块的数量等于机器上的核心数量.

每个chunck由一个线程处理.当所有线程完成后,你就完成了.借助.NET 4.0中ThreadPool的新功能(如果您使用Parallel.For或PLINQ或任务),您将获得一些其他好处,例如"偷工作"以进一步加快工作速度.

我认为Parallel.For/Parallel.ForEach对你很有用.

编辑

刚刚注意到.NET 3.5的要求.那些概念仍然适用,但你没有Parallel.For/ForEach.所以这是一个实现(从我的博客文章修改),它使用ThreadPool并使用数据并行技术.

    private static void SendEmailsUsingThreadPool(List<Recipient> recipients)
    {
      var coreCount = Environment.ProcessorCount;
      var itemCount = recipients.Count;
      var batchSize = itemCount / coreCount;

      var pending = coreCount;
      using (var mre = new ManualResetEvent(false))
      {
        for (int batchCount = 0; batchCount < coreCount; batchCount++)
        {
          var lower = batchCount * batchSize;
          var upper = (batchCount == coreCount - 1) ? itemCount : lower + batchSize;
          ThreadPool.QueueUserWorkItem(st =>
          {
            for (int i = lower; i < upper; i++)
              SendEmail(recipients[i]);
            if (Interlocked.Decrement(ref pending) == 0)
              mre.Set();
          });
        }
        mre.WaitOne();
      }      
    }

    private static void SendEmail(Recipient recipient)
    {
      //Send your Emails here
    }
  }

  class Recipient
  {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string EmailAddress { get; set; }
  }
Run Code Online (Sandbox Code Playgroud)

因此,获取您的数据并调用SendEmailUsingThreadPool()将数据传递给它.当然不要把你的方法称为:).如果您有DataSet/DataTable,则只需修改实现以接受DataSet/DataTable.此方法负责将数据分成块,因此您不必担心任何问题.简单地称呼它.