在多个线程上打开多个文件

时间:2013-01-14 16:10:37

标签: c# multithreading file

我必须处理大约170.000个文件,并且想要使用多个线程。 文件名按照Year-Number格式顺序排列,并按文件夹中的年份排序。 (但它们可以都在同一个文件夹中)。 不同年份有不同的文件数。文件很小,每个文件只有几个(10 <大小<20)KB。

处理它们的顺序是无关紧要的,因为处理任务的输出将存储在SQL数据库中。 最好的方法是什么?没有打开两次相同的文件?

5 个答案:

答案 0 :(得分:1)

可能的解决方案之一是使用生产者/消费者设计模式。

您的制作人将获得一个文件列表并提供一些ProducerConsumer队列。您的使用者将处理从队列中获取的文件(或文件路径)并对其进行处理(插入到您的数据库中)。使用这种方法,每个文件只会被处理一次。

C# producer/consumer SO问题中描述了ProducerConsumer队列的问题。

修改

然而,任务可能会变得复杂,例如

  • 如果其中一个现有文件发生变化,会发生什么。您是否必须使用新文件内容更新数据库?如果是这样,你必须有一个“ markers ”的机制,说文件已经改变(文件上次更新日期在某些情况下可能有效)
  • 如果在此过程中添加了新文件,会发生什么?等

答案 1 :(得分:0)

我说每年1个帖子。 每个'Year Thread'读取以该年份编号开头的文件,然后连续读取它们。 至于进入数据库,我建议你

  • 如果全部转到单个表,则删除索引以便不发生索引锁定,然后创建indes
  • 如果你不能删除索引,至少在超时之前使用行锁定和转换的等待时间(两个或多个线程可能同时插入)

另一个解决方案是,线程为文件生成insert语句,然后执行该文件以执行插入,或者您可以使用批量插入工具。但这取决于表结构和DBMS

答案 2 :(得分:0)

这是一个小例子:

public static class FilesProcessor
{
    private static List<FileProcessor> m_FileProcessors;

    public static void Start()
    {
        m_FileProcessors = new List<FileProcessor>();

        for (Int32 year = 2005; year < DateTime.Now.Year; ++year)
            InstanciateFileProcessor(year);

        while (!FinishedLoading())
            Application.DoEvents();
    }

    public static void Stop()
    {
        foreach (FileProcessor processor in m_FileProcessors)
            processor.Stop()

        m_FileProcessors.Clear();
        m_FileProcessors = null;
    }

    private static Boolean FinishedLoading()
    {
        foreach (FileProcessor processor in m_FileProcessors)
        {
            if (processor.IsAlive() && !processor.FinishedLoading())
                return false;
        }

        return true;
    }

    private static void InstanciateFileProcessor(Int32 year)
    {
        FileProcessor processor = new FileProcessor(year);
        processor.Start();

        m_FileProcessors.Add(processor);
    }
}

然后是FileProcessor类:

public sealed class FileProcessor
{
    private Int32 m_Year;

    public Boolean IsAlive()
    {
        return ((m_Thread != null) && m_Thread.IsAlive);
    }

    public Boolean FinishedLoading()
    {
        return ((m_Thread == null) || m_Thread.Join(10));
    }

    public FileProcessor(Int32 year)
    {
        m_Year = year;

        m_Thread = new Thread(Load);
        m_Thread.Name = "Background File Processor";
    }

    public void Start()
    {
        if (m_Thread != null)
            m_Thread.Start();
    }

    public void Stop()
    {
        if ((m_Thread != null) && m_Thread.IsAlive)
            m_Thread.Abort();
    }

    private void Load()
    {
        // Browse the Year folder...
        // Get and read all fines one by one...
    }
}

答案 3 :(得分:0)

我可以在这里看到两种可能的方法。

首先,将您的问题分成两部分。 1 - 确定要处理的内容,2 - 进行处理。第1部分可能必须自行运行,因此您最终会得到100%准确的需要处理的列表。然后,您可以实现关于拆分列表和引入多个线程的奇特(或非常非常奇特)的逻辑。

其次,做一些与@CarlosGrappa建议类似的事情。所以基本上你用自己的“预编程”过滤器创建每个线程。卡洛斯建议,这可能是一年。或者,您可以创建24个线程,每个小时一个文件时间戳。或者60个线程,每个线程查看一小时后的特定分钟。它基本上可以为您提供一个明确的标准,即(a)尽可能均匀地分割负载,以及(b)保证数据文件只处理一次。

显然,这些方法中的第二种方法运行得更快,但您必须更加深入地考虑如何拆分文件。使用第一种方法,一旦获得完整列表,您基本上可以在处理器中同时丢弃100个,1000个或10000个等文件,而不会过于聪明地使用它们。

答案 4 :(得分:0)

使用.Net的并行类有什么问题?

将一个集合传递给并行的foreach循环。 .Net为您完成所有分配。您还可以传入自定义分区程序,以便可以使用块分区。块分区导致线程不断要求更多任务。如果你不使用块分区,那么所有的工作都会被预先分配,导致某些任务比其他任务花费更长的时间(这可能会导致某些线程空闲,而一个线程仍有工作要做)。

http://msdn.microsoft.com/en-us/library/dd460720.aspx