C#中的FileStream和FileSystemWatcher,奇怪的问题是“进程无法访问文件". [英] FileStream and a FileSystemWatcher in C#, Weird Issue "process cannot access the file"

查看:235
本文介绍了C#中的FileStream和FileSystemWatcher,奇怪的问题是“进程无法访问文件".的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个复杂的代码库,它正在侦听某个文件夹上的FileCreated事件.创建文件后(还包括将文件移动到该文件夹​​),我想读入该文件并对其进行处理.它适用于第一个文件,但之后所有其他尝试均引发异常.在调试模式下(使用VisualStudio)会抛出该错误,但是如果我只单击继续",则它将起作用(没有错误).

I have this complicated code-base that is listening for FileCreated events on a certain folder. When the file is Created (which also includes moving a file to that folder), I want to read in that file and do something with it. It works for the first file, but throws an exception after for all other attempts. In Debug-mode (With VisualStudio) the error would be thrown, but if i just click "continue".. it would then work (without an error).

我已经发布了简化的代码来演示该问题.

I have posted simplified code, which demonstrates the issue.

例如,您启动应用程序,单击开始"按钮,然后创建新的文本文件"

For example, you start the application, click the "start" button, and then "create a new text file"

输出为:

Working

如果您随后以完全相同的方式创建第二个文件,则输出为:

If you then create a second file in the exact same manner, the output is:

Broken: The process cannot access the file 'C:\TestFolder\New Text Document (2).txt' because it is being used by another process.
Working, after breaking

查看完我的代码后,您将看到上面的打印输出集意味着,首先抛出了一个无法访问文件"异常,但是在catch语句中执行相同的调用突然起作用了.

After looking at my code, you will see that the above set of print-outs implies that first there was an "cannot access the file" exception thrown, but doing the same call in the catch-statement suddenly works.

这对我来说毫无意义,因为该文件显然没有被其他任何人使用(我刚刚创建了它)..而且无论如何它都能工作一秒钟....

This makes no sense to me, since the file is clearly not being used by anything else (i just created it).. and it works a second later anyhow....

XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" >
    <StackPanel>
        <Button Click="Button_Click"  Content="Start"/>
    </StackPanel>
</Window>

后面的代码:

using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;


namespace WpfApplication1
{ 
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            test();
        }


        String Folder = @"C:\TestFolder";

        private void test()
        {
            FileSystemWatcher watch = new FileSystemWatcher(Folder);
            watch.Created += new FileSystemEventHandler(FileCreated);
            watch.EnableRaisingEvents = true;

            Process.Start(Folder);
        }

        private void FileCreated(object sender, FileSystemEventArgs fsEvent)
        {

            if (File.Exists(fsEvent.FullPath))
            {

                // Thread.Sleep(1000);// Sleeping for 1 second seems to prevent the error from happening...?
                // If i am debugging, and pause on exceptions... then it also suddenly works (similar to the Sleep above)
                try
                {

                    FileStream fs = new FileStream(fsEvent.FullPath, FileMode.Open); 
                    Console.WriteLine("Working");
                    fs.Close();
                }
                catch (IOException ex)
                {
                    Console.WriteLine("Broken: " + ex.Message);
                    try
                    {                        
                        FileStream fs = new FileStream(fsEvent.FullPath, FileMode.Open);
                        Console.WriteLine("Working, after breaking");
                        fs.Close();

                    }
                    catch(IOException ex2)
                    {                        
                        FileStream fs = new FileStream(fsEvent.FullPath, FileMode.Open);
                        Console.WriteLine("really broken: " + ex2.Message);
                        fs.Close();
                    }
                }


            }
        }
    }
}

推荐答案

我从.NET 1.0开始就看到了您所描述的行为,并且从未费心找出它发生的原因.似乎OS或.NET有时(?)在调用close和dispose之后会在短时间内锁定文件.

I've seen the behavior you describe since .NET 1.0 and never bothered finding out why it happens. It seems like the OS or .NET sometimes(?) keep a lock on the file for a short time after you called close and dispose.

我做了一个解决方法-如果您愿意,也可以破解-对我们来说非常可靠.我们每天在服务器场中处理数百万个文件,并且文件观察者检测到的所有文件都将通过此方法,然后再移交给进一步处理.

I made a workaround - or hack if you like - that has proven to be very robust for us. We're processing millions of files daily in a server farm, and all files detected by filewatchers pass through this method before they are handed over for further processing.

它的作用是在文件上放置排他锁.如果失败,则可以选择最长等待10秒钟,以关闭文件再放弃.

What it does is to place an exclusive lock on the file. If it fails it will optionally wait up to 10 seconds for the file to be closed before giving up.

    public static bool IsFileClosed(string filepath, bool wait)
    {
        bool        fileClosed = false;
        int         retries = 20;
        const int   delay = 500; // Max time spent here = retries*delay milliseconds

        if (!File.Exists(filepath))
            return false;

        do
        {
            try 
            {
                // Attempts to open then close the file in RW mode, denying other users to place any locks.
                FileStream fs = File.Open(filepath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
                fs.Close();
                fileClosed = true; // success
            }
            catch (IOException) {}

            if (!wait) break;

            retries --;

            if (!fileClosed)
                Thread.Sleep( delay );
        }
        while (!fileClosed && retries > 0);

        return fileClosed;
    }

这篇关于C#中的FileStream和FileSystemWatcher,奇怪的问题是“进程无法访问文件".的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆