我想要读入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[]
答案 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
是线程安全的,所以你永远不应该两次处理同一个文件。
如果您有多个独立服务,则可以使用LockFile或File.Open
并指定FileShare.None
来查看。
编辑:
我误解了你想要做的事情。我以为你希望每个服务都处理所有文件。您确实需要在同一服务中运行这些多线程,或者允许某种通信方法允许不同的服务确定哪些文件已经被处理过。