我一直在研究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,因为它正被另一个进程使用。
发生此错误时,有时指示的文件存在或不存在。
因此,任何人都可以就为什么产生这些错误提供任何建议。我也不明白,据我所知,它应该可以工作(而且确实可以工作,一小段时间)。
来自 MSDN:
当需要对集合的每个元素或固定次数的迭代执行相同的独立操作时,请使用并行循环模式。如果循环的步骤不写入其他步骤读取的内存位置或文件,则循环的步骤是独立的。
Parallel.For
可以通过执行多线程来加快行的处理速度,但需要注意的是,如果它使用不正确,它将以程序的意外行为结束,就像您上面所说的那样。
出现以下错误的原因:
DirectoryNotFoundException:找不到路径"D:\Events\PATIENTID\EVENTID.txt"的一部分。
可能是一个线程去写入并且目录不存在意味着,而另一个线程创建它。通常,在进行并行处理时,可能会有竞争条件,因为我们在进行多线程处理,如果我们不使用适当的机制,如锁或监视器,那么我们最终会遇到此类问题。
当您进行文件写入时,尝试写入同一文件时多个线程最终会出现错误,即
该进程无法访问文件"D:\Events\PATIENTID\EVENTID.txt,因为它正被另一个进程使用。
由于一个线程已经在写入文件,因此此时其他线程将无法访问该文件以写入该文件。
我建议在这里使用普通循环而不是并行。