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



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

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

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

你的生产者将获得一个文件列表并提供一些ProducerConsumer队列。您的消费者将处理从队列中取出的文件(或文件路径)并处理它(插入到数据库中)。使用这种方法,每个文件只需处理一次。

在c#生产者/消费者SO问题中描述了ProducerConsumer队列问题。

编辑

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

  • 如果其中一个现有文件发生变化会发生什么。您必须用新的文件内容更新数据库吗?如果是这样,你必须有一个"标记"的机制,表明文件已经更改(文件最后更新日期在某些情况下可以工作)
  • 如果在过程中添加新文件会发生什么。等。

下面是一个小例子:

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...
    }
}

我觉得每年1个线程。每个"Year Thread"读取以年份号开头的文件,并依次读取它们。至于去数据库,我建议你要么

  • 如果所有到一个表,删除索引,这样就不会发生索引锁定,然后创建索引
  • 如果你不能删除索引,至少使用行锁定,并在超时之前等待一段时间的事务(两个或多个线程可能同时插入)

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

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

首先,把你的问题一分为二。1 -确定要加工什么,2 -加工。第1部分可能必须单独运行,以便您最终获得需要处理的内容的100%准确列表。然后,您可以在拆分列表和引入多个线程方面实现花哨(或不怎么花哨)的逻辑。

第二,做一些类似于@CarlosGrappa建议的事情。所以基本上你用自己的"预编程"过滤器创建每个线程。就像卡洛斯说的那样,这可能是今年。或者,您可以创建24个线程,每个线程代表文件时间戳的一个小时。或者60个线程,每个线程着眼于一小时后的某一分钟。它基本上可以是任何给你一个明确的标准:(a)尽可能平均地分配负载,(b)保证数据文件被处理一次且仅处理一次。

显然,第二种方法会运行得更快,但是您必须额外考虑如何分割文件。使用第一种方法,一旦您获得了完整的列表,您基本上可以一次在处理器中丢弃100、1000或10000等文件,而不必过于聪明地处理它。

使用。net的并行类有什么问题?

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

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

相关内容

  • 没有找到相关文章

最新更新