C# 如何在.net中并发处理目录中的文件
我在目录中并行处理文件时遇到问题。我读过几个类似的问题和例子,但我似乎找不到我的代码导致异常的原因 我的目录由其他进程填充,并且在任何时候都将包含数千个文件。每个文件都必须被解析和验证,这需要时间文件系统/网络io等。我需要并行完成这个步骤,其余的必须串行完成 这是我的密码:C# 如何在.net中并发处理目录中的文件,c#,.net,multithreading,C#,.net,Multithreading,我在目录中并行处理文件时遇到问题。我读过几个类似的问题和例子,但我似乎找不到我的代码导致异常的原因 我的目录由其他进程填充,并且在任何时候都将包含数千个文件。每个文件都必须被解析和验证,这需要时间文件系统/网络io等。我需要并行完成这个步骤,其余的必须串行完成 这是我的密码: public void run() { XmlMessageFactory factory = new XmlMessageFactory(); DirectoryInfo dir = new Direct
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}
}
public void run()
{
XmlMessageFactory=新的XmlMessageFactory();
DirectoryInfo dir=新的DirectoryInfo(m_sourceDir);
Dictionary retryList=新字典();
ConcurrentQueue MsgQueue=新建
ConcurrentQueue();
//启动worker来处理消息
System.Threading.ThreadPool.QueueUserWorkItem(o=>
{
味精;
元组项;
while(true)
{
如果(!MsgQueue.TryDequeue(out项))
{
系统线程线程睡眠(5000);
继续;
}
尝试
{
msg=item.Item1;
/*对msg的处理发生在这里*/
handleMessageProcessed(item.Item2,参考retryList);
}
捕获(例外e)
{
//如果调用此方法,则会给出
//以下例外情况
handleMessageFailed(item.Item2,例如ToString());
}
}
}
);
while(true)
{
尝试
{
FileInfo[]files=dir.GetFiles(m_文件类型);
Partitioner Partitioner=Partitioner.Create(files,true);
Parallel.ForEach(partitioner,f=>
{
尝试
{
XmlMsg msg=factory.getMessage(messageType);
尝试
{
msg.loadFile(f.FullName);
Enqueue(新元组(msg,f));
}
捕获(例外e)
{
handleMessageFailed(f,e.ToString());
}
}
});
}
}
}
静态无效handleMessageFailed(文件信息f,字符串消息)
{
//请点击此处:
f、 移动到(m_failedDir+f.Name);
//“进程无法访问该文件,因为它是
//正在被另一个进程使用。“}System.Exception{System.IO.IOException}
}
使用ConcurrentQueue,它怎么会同时尝试访问一个文件两次呢?
我目前有一个5000个文件的测试设置,每次运行至少会发生一次,每次都在不同的文件上。当我检查目录时,导致异常的源文件将已被处理,并且位于“已处理”目录中。我怀疑
XmlMsg.loadFile()中有问题。
我认为您可能有这样的代码:
public void loadFile(string filename)
{
FileStream file = File.OpenRead(filename);
// Do something with file
file.Close();
}
如果“对文件执行某些操作”部分出现异常,则不会关闭该文件,因为永远不会执行file.Close()
。然后,您将在handleMessageFailed()
中获得“正在使用的文件”异常
如果是这样,解决方案是使用块访问中的文件,如下所示;然后,即使发生异常,它也将关闭:
public void loadFile(string filename)
{
using (FileStream file = File.OpenRead(filename))
{
// Do something with file
}
}
但是假设这确实是问题所在,当您开始使用外部进程生成的真实文件时,如果您的工作线程尝试处理这些文件时,外部进程仍然打开这些文件,那么您可能会遇到另一个问题。经过一番努力,问题变得非常简单!发生的情况是,目录中文件的并行处理在文件的串行活动之前完成,因此循环正在重新启动,并将一些已经在那里的文件重新添加到队列中
为了完整起见,这里是代码的修改部分:
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);
}
}
}
while(true)
{
尝试
{
FileInfo[]files=dir.GetFiles(m_文件类型);
Partitioner Partitioner=Partitioner.Create(files,true);
Parallel.ForEach(partitioner,f=>
{
尝试
{
XmlMsg msg=factory.getMessage(messageType);
尝试
{
msg.loadFile(f.FullName);
Enqueue(新元组(msg,f));
}
捕获(例外e)
{
handleMessageFailed(f,e.ToString());
}
}
});
//添加了等待队列耗尽的检查
//重新扫描目录
而(MsgQueue.Count>0)
{
系统线程线程睡眠(5000);
}
}
}
当前的硬盘不能很好地进行并行处理,因此如果文件太大,它可能成为瓶颈。否则,您可以划分从文件系统加载文件的逻辑,然后将要处理的数据推送到新队列中,以便并行处理它们。我明白,HDD访问不是瓶颈,而是调用loadFile()
从网络中提取文件等,因此我希望一次执行其中几个。我认为在某些情况下,当一个工作线程试图对该文件执行某些操作时,创建文件的进程很可能仍在写入该文件。你对文件制作者有控制权吗?如果没有,最好让线程休眠一段时间,然后重试——这一点都不好。@MatthewWatson我正在运行应用程序之前,使用目录中的一组文件进行测试。有时我也会“找不到s”