代码之家  ›  专栏  ›  技术社区  ›  supermeerkat

嵌套并行。For()循环和文件创建问题

  •  1
  • supermeerkat  · 技术社区  · 6 年前

    我一直在研究TPL作为快速生成大量文件的方法——我在一个数据库中有大约1000万行,这些事件属于患者,我想输出到他们自己的文本文件中,位置为d:\events\PATIENTID\EVENTID。txt文件

    我使用的是两个嵌套的并行。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);
                });
        }
    
    });
    

    我生成的代码运行速度很快,但几秒钟后就会产生错误(其中大约生成6000个文件)。产生的错误属于以下两种类型之一:

    DirectoryNotFoundException:找不到路径“D:\Events\PATIENTID\EVENTID”的一部分。txt'。

    每当产生此错误时,目录结构D:\Events\PATIENTID\就会存在,因为该目录中已创建了其他文件。在进入第二个循环之前,if条件检查是否存在D:\Events\PATIENTID\。

    进程无法访问文件“D:\Events\PATIENTID\EVENTID”。txt”,因为它正被另一个进程使用。

    发生此错误时,有时指示的文件存在或不存在。

    那么,有谁能给我们提供一些关于为什么会产生这些错误的建议呢。我也不明白,就我所知,它应该会起作用(而且确实会起作用,在短时间内)。

    1 回复  |  直到 6 年前
        1
  •  3
  •   Ehsan Sajjad    6 年前

    From MSDN:

    当需要对集合的每个元素或固定次数的迭代执行相同的独立操作时,请使用并行循环模式。 如果循环的步骤不写入其他步骤读取的内存位置或文件,则循环的步骤是独立的。

    Parallel.For 可以通过执行多线程来加快行的处理速度,但需要注意的是,如果使用不正确,将导致程序出现意外行为,就像上面的程序一样。

    出现以下错误的原因:

    DirectoryNotFoundException:找不到路径“D:\Events\PATIENTID\EVENTID”的一部分。txt'。

    可能是一个线程去写,而目录不在那里,这意味着另一个线程创建了它。通常,在执行并行时,可能会出现争用条件,因为我们正在执行多线程,如果我们没有使用适当的机制,如锁或监视器,那么我们最终会遇到此类问题。

    当您在写文件时,多个线程试图写入同一个文件时,最终会出现后一个错误,即。

    进程无法访问文件“D:\Events\PATIENTID\EVENTID”。txt”,因为它正被另一个进程使用。

    因为一个线程已经在写入文件,所以此时其他线程将无法访问该文件以进行写入。

    我建议在这里使用普通循环,而不是并行。