我在目录中并行处理文件时遇到问题。我已经阅读了几个类似的问题和例子,但我似乎无法找到为什么我的代码会导致异常。
我的目录由其他进程填充,任何时候都会包含数千个文件。每个文件都必须进行解析和验证,这需要时间文件系统/网络等。我需要这个步骤并行完成,其余的必须连续完成。
这是我的代码:
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个文件的测试设置,每次运行至少会发生一次,每次都会在不同的文件上发生。当我检查目录时,导致异常的源文件将被处理并位于“已处理”目录中。
答案 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>