C#文件移动(重命名)多线程Windows操作系统

时间:2013-11-15 18:10:53

标签: c# multithreading windows-services

我想要读入10,000个XML文件并保存到数据库的场景。我所拥有的是5个Windows服务,它们都在尝试和处理该文件夹。

我的技术是首先尝试使用特定于给定服务实例的扩展名重命名(File.Move)文件。

99%的时间都在工作。然而,我所看到的是文件系统将.01%的时间允许两个请求尝试在同一时间重命名。

我该怎样防止这种情况?这有意义吗?请参阅以下代码段以了解相关信息。我最终得到了10个IO例外文件。

 string[] sourceFiles = Directory.GetFiles(InputPath, string.Format(LocaleHelper.Culture, "*.{0}", Extention))
                                            .OrderBy(d => new FileInfo(d).CreationTime).ToArray();


            foreach (string file in sourceFiles)
            {
                var newFileName = string.Format(LocaleHelper.Culture, "{0}.{1}", file, CacheFlushManager.GetInstanceName);


                try
                {
                    // first we'll rename // however at this point the file may not even exist
                    // it will throw an exception and move onto the next file if it exists


                    File.Move(file, newFileName);



                    var xml = File.ReadAllText(newFileName);

                    // write to DB at this point we know its unique
                }
                catch (FileNotFoundException ex)
                {
                    Logger.LogDebug(string.Format(LocaleHelper.Culture, "{0} Couldn't read file : {1}", CacheFlushManager.GetInstanceName, newFileName));
                }
                catch (IOException ex)
                {
                    Logger.LogDebug(string.Format(LocaleHelper.Culture, "{0} Couldn't process file : {1}", CacheFlushManager.GetInstanceName, newFileName));
                }
                catch (Exception ex)
                {
                    Logger.LogError("Execute: Error", ex);

                    try
                    {
                        File.Move(newFileName, string.Format(LocaleHelper.Culture, "{0}.badfile", newFileName));
                    }
                    catch (Exception ex_deep)
                    {
                        Logger.LogError(string.Format("{0} Execute: Error Deep could not move bad file {1}", CacheFlushManager.GetInstanceName, newFileName), ex_deep);
                    }
                }

编辑1

下面是确切的错误,作为我所看到的一个例子。基于我正在使用的代码,我对文件如何根据我使用的代码使用确切时间感到困惑?我是否完全没有杂草?

  

[7220] TransactionFileServiceProcess [11:28:32]:Service4无法处理文件:C:\ temp \ Input \ yap804.xml.Service4 System.IO.IOException:进程无法访问文件'C:\ temp \ Input \ yap804.xml.Service4'因为它正由另一个进程使用。

编辑2

以下是从“调试”角度看一下发生了什么。服务2& 2怎么样? 3到“结束重命名?”我认为这是问题的关键......想法?

问题出在文件yap620.xml.Service3,因为文件操作错误,最终只会坐在那里。

[6708] TransactionFileServiceProcess [10:54:38]: Service3 Start Rename: C:\temp\Input\yap620.xml.Service3 TransactionFileServiceProcess.Execute => BHSLogger.LogDebug =>     LoggerImpl.Write E[]

[4956] TransactionFileServiceProcess [10:54:38]: Service2 Start Rename: C:\temp\Input\yap620.xml.Service2 TransactionFileServiceProcess.Execute => BHSLogger.LogDebug => LoggerImpl.Write E[]

[7416] TransactionFileServiceProcess [10:54:38]: Service4 Start Rename: C:\temp\Input\yap620.xml.Service4 TransactionFileServiceProcess.Execute => BHSLogger.LogDebug => LoggerImpl.Write E[]

[6708] TransactionFileServiceProcess [10:54:38]: Service3 End Rename: C:\temp\Input\yap620.xml.Service3 TransactionFileServiceProcess.Execute => BHSLogger.LogDebug => LoggerImpl.Write E[]

[6708] TransactionFileServiceProcess [10:54:38]: Service3 Start Read: C:\temp\Input\yap620.xml.Service3 TransactionFileServiceProcess.Execute => BHSLogger.LogDebug => LoggerImpl.Write E[]

[4956] TransactionFileServiceProcess [10:54:38]: Service2 End Rename: C:\temp\Input\yap620.xml.Service2 TransactionFileServiceProcess.Execute => BHSLogger.LogDebug => LoggerImpl.Write E[]

[4956] TransactionFileServiceProcess [10:54:38]: Service2 Start Read: C:\temp\Input\yap620.xml.Service2 TransactionFileServiceProcess.Execute => BHSLogger.LogDebug => LoggerImpl.Write E[]

[6708] TransactionFileServiceProcess [10:54:38]: Service3 Couldn't process file : C:\temp \Input\yap620.xml.Service3 TransactionFileServiceProcess.Execute => BHSLogger.LogDebug => LoggerImpl.Write E[]

2 个答案:

答案 0 :(得分:1)

我不知道问题出在哪里。您有多个获取文件列表的线程,然后尝试处理这些文件。有时,线程尝试重命名的文件不存在,有时文件存在,但它正在被另一个线程重命名。这两者中的任何一个都不应成为问题。在任何一种情况下,获取错误的线程都应该假设某个其他线程正在处理该文件,然后继续。

当然,假设您没有其他进程访问该目录中的文件。

为什么你需要五个独立的服务实例来做这件事超出我的意思。你可以简化一些事情,只需要一个进程执行Parallel.ForEach就可以减少不必要的I / O.例如:

string[] sourceFiles = Directory.GetFiles(
    InputPath,
    string.Format(LocaleHelper.Culture, "*.{0}", Extention))
    .OrderBy(d => new FileInfo(d).CreationTime).ToArray();

Parallel.Foreach(sourceFiles, (file) =>
{
    // do file processing here
});

TPL将分配多个线程来进行处理,并将工作项分配给线程。因此,多个线程不可能打开文件。

答案 1 :(得分:0)

您是否在同一服务中运行多个线程?还是多个独立的服务?

如果在同一服务中有多个线程,只需创建一个Queue<FileInfo>或类似的东西,并在线程可以自由处理时从队列中删除项目。我相信标准Queue是线程安全的,所以你永远不应该两次处理同一个文件。

如果您有多个独立服务,则可以使用LockFileFile.Open并指定FileShare.None来查看。

编辑:

我误解了你想要做的事情。我以为你希望每个服务都处理所有文件。您确实需要在同一服务中运行这些多线程,或者允许某种通信方法允许不同的服务确定哪些文件已经被处理过。