我一直在调查TPL作为快速生成大量文件的方法 - 我在数据库中有大约1000万行,属于患者的事件,我想输出到他们自己的文本文件中,在该位置d:\ EVENTS \ PATIENTID \ EVENTID.txt
我正在使用两个嵌套的Parallel.ForEach循环 - 检索患者列表的外部以及检索患者事件并将其写入文件的内部。
这是我正在使用的代码,目前非常粗糙,因为我只是想让事情发挥作用。
DataSet1TableAdapters.GetPatientsTableAdapter ta = new DataSet1TableAdapters.GetPatientsTableAdapter();
List<DataSet1.GetPatientsRow> Pats = ta.GetData().ToList();
List<DataSet1.GetPatientEventsRow> events = null;
string patientDir = null;
System.IO.DirectoryInfo di = new DirectoryInfo(txtAllEventsPath.Text);
di.GetDirectories().AsParallel().ForAll((f) => f.Delete(true));
//get at the patients
Parallel.ForEach(Pats
, new ParallelOptions() { MaxDegreeOfParallelism = 8 }
, patient =>
{
patientDir = "D:\\Events\\" + patient.patientID.ToString();
//Output directory
Directory.CreateDirectory(patientDir);
events = new DataSet1TableAdapters.GetPatientEventsTableAdapter().GetData(patient.patientID).ToList();
if (Directory.Exists(patientDir))
{
Parallel.ForEach(events.AsEnumerable()
, new ParallelOptions() { MaxDegreeOfParallelism = 8 }
, ev =>
{
List<DataSet1.GetAllEventRow> anEvent =
new DataSet1TableAdapters.GetAllEventTableAdapter();
File.WriteAllText(patientDir + "\\" + ev.EventID.ToString() + ".txt", ev.EventData);
});
}
});
我生成的代码工作得非常快,但在几秒钟后产生错误(其中产生了大约6,000个文件)。产生的错误是以下两种类型之一:
DirectoryNotFoundException:找不到路径'D:\ Events \ PATIENTID \ EVENTID.txt'的一部分。
每当产生此错误时,目录结构D:\ Events \ PATIENTID \都存在,因为已在该目录中创建了其他文件。在输入第二个循环之前,if条件检查是否存在D:\ Events \ PATIENTID \。
该进程无法访问文件'D:\ Events \ PATIENTID \ EVENTID.txt',因为它正由另一个进程使用。
发生此错误时,有时指示的文件存在或不存在。
那么,任何人都可以提出关于为什么会产生这些错误的建议。我也不明白,到目前为止我可以看到,它应该正常工作(确实会这么做)。
答案 0 :(得分:2)
当您需要对集合的每个元素或固定数量的迭代执行相同的独立操作时,请使用并行循环模式。 如果循环步骤不写入内存位置或其他步骤读取的文件,则循环步骤是独立的。
Parallel.For
可以通过执行多线程来加快对行的处理,但它需要注意的是,如果使用不正确,它将以程序的意外行为结束,就像你上面的程序一样。
出现以下错误的原因:
DirectoryNotFoundException:找不到路径的一部分&#39; D:\ Events \ PATIENTID \ EVENTID.txt&#39;。
可以是一个线程写入并且该目录不存在,而另一个线程创建它。通常情况下,当进行并行操作时,可能存在竞争条件,因为我们正在进行多线程,如果我们不使用锁或监视器等适当的机制,那么我们最终会遇到这类问题。
当你正在进行文件写入时,多个线程在尝试写入同一个文件时最终会出现错误,即后者,即
该过程无法访问文件&#39; D:\ Events \ PATIENTID \ EVENTID.txt&#39;因为它正被另一个进程使用。
因为一个线程已经写入文件,所以当时其他线程无法访问该文件以写入该文件。
我建议在这里使用普通循环而不是并行。