经过一些测试后,我不确定我是否正确处理了问题,因为使用Parallel.ForEach
运行程序需要的时间比foreach
多。我把它分成几步:
阅读*.eml
文件:
var newMailMessage = Sasa.Net.Mail.Message.Parse(File.ReadAllText(config.TemplateFilePath));
加载收件人列表:
var recipients = RecipientHelper.LoadFromFile(config.MailListFilePath);
将此列表拆分为块:
var recipientChunks = recipients.Chunk(config.MessagesPerBlock.Value);
然后我在做:
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
连接发送。
答案 0 :(得分:1)
问题在于这行代码:
lock (syncRoot)
由于这个对象似乎与这些循环中的任何东西无关,也没有在这些循环中构造,因此它必须是外部的,因此在所有并行任务中都是相同的对象。
因此,该锁内的代码将按顺序运行。