Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/321.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在.net中并发处理目录中的文件_C#_.net_Multithreading - Fatal编程技术网

C# 如何在.net中并发处理目录中的文件

C# 如何在.net中并发处理目录中的文件,c#,.net,multithreading,C#,.net,Multithreading,我在目录中并行处理文件时遇到问题。我读过几个类似的问题和例子,但我似乎找不到我的代码导致异常的原因 我的目录由其他进程填充,并且在任何时候都将包含数千个文件。每个文件都必须被解析和验证,这需要时间文件系统/网络io等。我需要并行完成这个步骤,其余的必须串行完成 这是我的密码: public void run() { XmlMessageFactory factory = new XmlMessageFactory(); DirectoryInfo dir = new Direct

我在目录中并行处理文件时遇到问题。我读过几个类似的问题和例子,但我似乎找不到我的代码导致异常的原因

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

这是我的密码:

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”