【发布时间】:2016-01-28 09:52:51
【问题描述】:
我需要在一夜之间处理大量文件,并确定开始和结束时间,以避免干扰用户。我一直在调查,但现在处理线程的方法太多了,我不确定该走哪条路。这些文件作为附件进入 Exchange 收件箱。
基于此处的一些示例和一些实验,我目前的尝试是:
while (DateTime.Now < dtEndTime.Value)
{
var finished = new CountdownEvent(1);
for (int i = 0; i < numThreads; i++)
{
object state = offset;
finished.AddCount();
ThreadPool.QueueUserWorkItem(delegate
{
try
{
StartProcessing(state);
}
finally
{
finished.Signal();
}
});
offset += numberOfFilesPerPoll;
}
finished.Signal();
finished.Wait();
}
为了方便起见,它目前在 winforms 应用程序中运行,但核心处理在 dll 中,因此我可以从 Windows 服务、在调度程序下运行的控制台生成我需要的类,但这是最简单的。我确实设置了一个带有 Timer 对象的 Windows 服务,该对象在配置文件中设置的时间启动处理。
所以我的问题是——在上面的代码中,我初始化了一堆线程(目前是 10 个),然后等待它们全部处理。我的理想是静态数量的线程,当一个线程完成时,我触发另一个线程,然后当我到达结束时间时,我只等待所有线程完成。 这样做的原因是我正在处理的文件是可变大小的 - 有些可能需要几秒钟来处理,有些可能需要几个小时,所以我不希望整个应用程序等待一个线程完成,如果我可以让它滴答作响在后台。 (编辑)就目前而言,每个线程实例化一个类并传递一个偏移量。然后该类从收件箱中获取下 x 封电子邮件,从偏移量开始(使用 Exchange Web 服务分页功能)。随着每个文件的处理,它被移动到一个单独的文件夹中。从到目前为止的一些回复中,我想知道我是否真的应该在外循环中获取电子邮件,并根据需要生成线程。 为了解决这个问题,我目前正在尝试处理积压的电子邮件。清除积压后,夜间运行的负载可能会显着降低。
平均每晚要处理大约 1000 个文件。
更新
我重写了大部分代码,以便可以使用 Parallel.Foreach,但我遇到了线程安全问题。调用代码现在如下所示:
public bool StartProcessing()
{
FindItemsResults<Item> emails = GetEmails();
var source = new CancellationTokenSource(TimeSpan.FromHours(10));
// Process files in parallel, with a maximum thread count.
var opts = new ParallelOptions { MaxDegreeOfParallelism = 8, CancellationToken = source.Token };
try
{
Parallel.ForEach(emails, opts, processAttachment);
}
catch (OperationCanceledException)
{
Console.WriteLine("Loop was cancelled.");
}
catch (Exception err)
{
WriteToLogFile(err.Message + "\r\n");
WriteToLogFile(err.StackTrace + "r\n");
}
return true;
}
到目前为止一切顺利(请原谅临时错误处理)。我现在有一个新问题,即作为电子邮件的“Item”对象的属性不是线程安全的。因此,例如,当我开始处理一封电子邮件时,我将其移动到“处理”文件夹中,以便另一个进程无法抓取它 - 但事实证明,多个线程可能正在尝试处理同一封电子邮件一次。我如何保证不会发生这种情况?我知道我需要添加一个锁,我可以在 ForEach 中添加它还是应该在 processAttachments 方法中添加它?
【问题讨论】:
-
using a thread pool 怎么样?
-
时间用完了怎么办?你只是停止处理文件吗?处理每个文件与从磁盘读取文件需要多长时间?
-
@Enigmativity - 我实际上正在处理收件箱中电子邮件的附件。不知道你的比较是什么意思?该处理涉及解析附件内容并传递给服务。这需要的时间长度取决于文件的大小和复杂性。首先将文件保存到磁盘并没有显着的收益。回答第一个问题 - 当我用完时间时,我想停止生成新线程,并等待当前线程完成。
-
@EmmaFaulkner - 比较的是加载文件所需的时间(IO 并没有从多线程中得到很大改善)与处理时间(CPU 被多线程大大提高)。
-
@Enigmativity 啊,我明白了。是的,我正在使用多线程来节省时间。在我的情况下,加载文件所花费的时间不是问题,而是解析。电子邮件在一夜之间流入收件箱,因此在每次通过时,我可能有许多线程不做任何事情(或者线程数少于最大值,更正确,但相反我可能有一个或两个需要很长时间处理时间。
标签: c# multithreading timer batch-processing