如何在.net中同时处理目录中的文件

时间:2014-01-27 15:18:58

标签: c# .net multithreading

我在目录中并行处理文件时遇到问题。我已经阅读了几个类似的问题和例子,但我似乎无法找到为什么我的代码会导致异常。

我的目录由其他进程填充,任何时候都会包含数千个文件。每个文件都必须进行解析和验证,这需要时间文件系统/网络等。我需要这个步骤并行完成,其余的必须连续完成。

这是我的代码:

public void run()
{
    XmlMessageFactory factory = new XmlMessageFactory();
    DirectoryInfo dir = new DirectoryInfo(m_sourceDir);
    Dictionary<string, int> retryList = new Dictionary<string, int>();
    ConcurrentQueue<Tuple<XmlMsg,FileInfo>> MsgQueue = new
                                      ConcurrentQueue<Tuple<XmlMsg,FileInfo>>();

    //start worker to handle messages
    System.Threading.ThreadPool.QueueUserWorkItem(o =>
        {
            XmlMsg msg;
            Tuple<XmlMsg, FileInfo> item;
            while (true)
            {
                if (!MsgQueue.TryDequeue(out item))
                {
                    System.Threading.Thread.Sleep(5000);
                    continue;
                }
                try
                {
                    msg = item.Item1;
                    /* processing on msg happens here */
                    handleMessageProcessed(item.Item2, ref retryList);
                }
                catch (Exception e)
                {
                    //if this method is called it gives the 
                    //exception below
                    handleMessageFailed(item.Item2, e.ToString()); 
                }
            }
        }
    );

    while (true)
    {
        try
        {
            FileInfo[] files = dir.GetFiles(m_fileTypes);
            Partitioner<FileInfo> partitioner = Partitioner.Create(files, true);
            Parallel.ForEach(partitioner, f => 
            {
                try
                {
                    XmlMsg msg = factory.getMessage(messageType);
                    try
                    {
                        msg.loadFile(f.FullName);
                        MsgQueue.Enqueue(new Tuple<XmlMsg, FileInfo>(msg, f));
                    }
                    catch (Exception e)
                    {
                        handleMessageFailed(f, e.ToString());
                    }
                }
            });
        }
    }
}

static void handleMessageFailed(FileInfo f, string message)
{
    //Erorr here: 
    f.MoveTo(m_failedDir + f.Name);
    //"The process cannot access the file because it is 
    //being used by another process."}  System.Exception {System.IO.IOException}
}

使用ConcurrentQueue如何最终同时尝试两次访问文件?

我目前有一个包含5000个文件的测试设置,每次运行至少会发生一次,每次都会在不同的文件上发生。当我检查目录时,导致异常的源文件将被处理并位于“已处理”目录中。

2 个答案:

答案 0 :(得分:1)

经过一段时间的努力之后,问题变得非常简单!发生的事情是目录中文件的并行处理在文件上的串行活动之前完成,因此循环重新启动并将一些文件重新添加到已经存在的队列中。

为了完整性,这里修改了代码部分:

while (true)
    {
        try
        {
            FileInfo[] files = dir.GetFiles(m_fileTypes);
            Partitioner<FileInfo> partitioner = Partitioner.Create(files, true);
            Parallel.ForEach(partitioner, f => 
            {
                try
                {
                    XmlMsg msg = factory.getMessage(messageType);
                    try
                    {
                        msg.loadFile(f.FullName);
                        MsgQueue.Enqueue(new Tuple<XmlMsg, FileInfo>(msg, f));
                    }
                    catch (Exception e)
                    {
                        handleMessageFailed(f, e.ToString());
                    }
                }
            });
            //Added check to wait for queue to deplete before 
            //re-scanning the directory
            while (MsgQueue.Count > 0)
            {
                System.Threading.Thread.Sleep(5000);
            }
        }
    }

答案 1 :(得分:0)

我怀疑XmlMsg.loadFile()

中存在问题

我认为你可能会有这样的代码:

public void loadFile(string filename)
{
    FileStream file = File.OpenRead(filename);

    // Do something with file

    file.Close();
}

如果&#34中发生异常,请执行文件&#34;部分,文件不会被关闭,因为永远不会执行file.Close()。然后,您将获得正在使用的&#34;文件&#34; handleMessageFailed()内的异常。

如果是这样,解决方案是访问using块中的文件,如下所示;然后即使发生异常也会关闭:

public void loadFile(string filename)
{
    using (FileStream file = File.OpenRead(filename))
    {
        // Do something with file
    }
}

但是假设这确实是问题所在,当您开始使用外部进程生成的真实文件时,如果外部进程在您的工作线程尝试处理它们时仍然打开文件,则可能会遇到另一个问题。 / p>