使用Parallel.ForEach发送电子邮件

时间:2017-11-09 11:35:51

标签: c# smtpclient parallel.foreach

经过一些测试后,我不确定我是否正确处理了问题,因为使用Parallel.ForEach运行程序需要的时间比foreach多。我把它分成几步:

  1. 阅读*.eml文件:

    var newMailMessage = Sasa.Net.Mail.Message.Parse(File.ReadAllText(config.TemplateFilePath));

  2. 加载收件人列表:

    var recipients = RecipientHelper.LoadFromFile(config.MailListFilePath);

  3. 将此列表拆分为块:

    var recipientChunks = recipients.Chunk(config.MessagesPerBlock.Value);

  4. 然后我在做:

    foreach (var chunk in recipientChunks)
                    {
                        Parallel.ForEach(chunk.AsParallel(), new ParallelOptions { MaxDegreeOfParallelism = recipientChunks.Count() },
                            (recipient, state, index) =>
                        {
                            using (var client = new SmtpClient(config.SmtpHost, config.SmtpPort.Value))
                            {
                                lock (syncRoot)
                                {
    var mailMessage = new Postman(recipient, newMailMessage, config).PrepareMail();
    client.Send(mailMessage);
                                }
                            }
                        }
                    }
    

    我已经用15,000封电子邮件测试了这段代码,并且花了2.5分钟发送所有内容,但是当我使用foreach进行此操作时花了2分钟。如何改进此代码?

    我希望每个chunk都有一个SMTP连接,这些块应该并行发送。

    EDITED

    好的如果我删除了lock我的邮件没有正确准备,例如我的流有问题,我已经使用Stream.Synchronized(linkedResource.ContentStream),电子邮件有不同的大小等等。所以如何处理此?

    Chunk的源代码:

    public static IEnumerable<T[]> Chunk<T>(this IEnumerable<T> items, int size)
        {
            T[] array = items as T[] ?? items.ToArray();
            for (int i = 0; i < array.Length; i += size)
            {
                T[] chunk = new T[Math.Min(size, array.Length - i)];
                Array.Copy(array, i, chunk, 0, chunk.Length);
                yield return chunk;
            }
        }
    

    Parallel.ForEach是否适合I/O操作?应该在这种情况下使用吗?

    每个块的所有电子邮件都应通过不同的SMTP连接发送。

1 个答案:

答案 0 :(得分:1)

问题在于这行代码:

lock (syncRoot)

由于这个对象似乎与这些循环中的任何东西无关,也没有在这些循环中构造,因此它必须是外部的,因此在所有并行任务中都是相同的对象。

因此,该锁内的代码将按顺序运行。