【问题标题】:Deleting/creating files throws System.UnauthorizedAccessException after a lot of iterations多次迭代后删除/创建文件会引发 System.UnauthorizedAccessException
【发布时间】:2019-02-05 08:15:08
【问题描述】:

在测试有关写入/创建/删除文件的一些事情时,我制作了以下程序,该程序在 n 次循环中删除和创建文件。

static string path = @"C:\Users\Remy\Desktop\Testing";
static readonly int sampleSize = 10000; // Amount of iterations the methods will be run for.

static byte[] sourceFile;

static void Main(string[] args)
{
    using (FileStream fs = new FileStream(path + @"\SourceFile.txt", FileMode.Open, FileAccess.Read))
    {
        sourceFile = new byte[fs.Length];
        fs.Read(sourceFile, 0, sourceFile.Length);
    }

    string filePath = path + @"\Destination.txt";
    for (int i = 0; i < sampleSize; i++)
    {
        if (File.Exists(filePath))
        {
            File.SetAttributes(filePath, FileAttributes.Normal);
            File.Delete(filePath);//Error sometimes throws here.
        }

        using (FileStream file = File.OpenWrite(filePath))
        {
            file.Write(sourceFile, 0, sourceFile.Length);
        }
    }

    Console.ReadLine();
}

当迭代次数不太高(大约 1000 次)时,该程序大部分时间都按预期工作。它将删除旧文件并创建一个新文件。

但是,当我将迭代次数增加到 10000/100000 时,会出现问题,在极少数情况下(大约 0.03% 的时间)它会在 using (FileStream file = File.OpenWrite(filePath)) 处抛出 System.UnauthorizedAccessException,而其他 99.97% 的时间则成功通过。当错误抛出时,文件不会被创建。

这发生在使用调试/发布的 VS(以管理员身份)和以管理员身份运行的构建 .exe 中。

在查看此问题时,我发现以下有关 Unauth... 异常的答案。

  • this 答案建议设置属性,但如我的示例中所示,我已经这样做了。
  • this 和其他一些答案建议以管理员权限运行应用程序。我已经在做同样的事情了。

我还添加了父文件夹的权限,以允许 full controlEveryone 对所有文件和子文件夹。

起初我认为我创建的文件可能不够大(目前正在写入 976kb 的随机数据),并且由于某种原因,程序迭代创建/删除的速度比操作系统/硬盘可以处理的快。但是当增加文件大小时会发生同样的行为

我已经在 3 台机器上对其进行了测试,并且都发生了这种情况。

这可能是 Windows 因误报而引发异常的情况吗?看到它只发生在大迭代上?我在这里错过了什么完全不同的东西吗?

注意:我不是在寻找处理异常的方法。我可以处理。我正在寻找的原因为什么会发生这种奇怪的行为,如果可能的话防止它而不是治愈


环境

我正在写入的磁盘是 Crucial 英睿达 MX300 SSD,使用 sata 3 没有 RAID。 16 GB 内存。操作系统 Windows 10(专业版)64 位。运行程序时系统尽可能空闲。

控制台应用程序的目标是 .NET Framework 4.6.1 构建,使用 Visual Studio 2017 和 Release Any CPU 设置。


我根据评论建议尝试过的其他事情:

我尝试在创建和删除后添加Thread.Sleep,以确保 Windows 能够清除文件缓存。这仍然会引发异常,但这次它会在 File.Delete(filePath); 上引发异常。

关闭 Windows Defender 等也会导致在File.Delete(filePath) 而不是using(FIleStream....) aswel 上引发错误。

改用以下内容写入文件:

using (FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Write, FileShare.None)) 
{ 
    file.Write(sourceFile, 0, sourceFile.Length); file.Flush(flushToDisk: true); 
}

也会产生同样的异常被抛出

【问题讨论】:

  • 是否在其间的目录中创建文件?
  • 我最好的猜测是 Windows 将写入缓存到文件表中,并且在某些时候无法跟上您快速删除和创建的速度。问题是否也会出现一小段延迟,比如删除和写入/设置属性之间的 50 毫秒? Thread.Sleep(50)
  • 第一次后是否继续弹出错误? (当你抓住它并继续循环时)还是它只弹出一次,然后在另一次迭代后再次弹出? string filePath = ... 也应该在循环之外,如果你想在本地放置一个额外的范围。
  • @remy_rm 您的 PC 上是否安装了任何防病毒软件,包括 Windows Defender、Microsoft Essentials 等?
  • @remy_rm,你的是什么?我认为您应该使用不在您的解决方案文件夹内的文件夹 - 例如独立的“D:\文件夹”。如果您的路径放在解决方案文件夹中,它可能由 VS 和\或 Git 控制,可以检查新添加的文件。也尝试在 Release 配置中运行应用程序并且\或不在 VS 下运行。

标签: c# file filestream


【解决方案1】:

这种问题是没有办法摆脱的,唯一的处理办法就是防止或避免,如果操作失败,做一些日志。如果失败,请多次尝试这样的操作。之前我已经创建了一个,这里是代码sn-p。

public IList<string> ReadLineByLineFromFile( string filePath )
{
    const int numberOfRetries = 3;
    const int delayOnRetry = 500;

    bool success = false;
    List<string> logs = null;

    for ( int i = 0; i <= numberOfRetries && success == false; i++ )
    {
        try
        {
            logs = new List<string>();

            const Int32 bufferSize = 128;
            using ( var fileStream = File.Open( filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ) )
            using ( var streamReader = new StreamReader( fileStream, Encoding.UTF8, true, bufferSize ) )
            {
                string line;

                while ( ( line = streamReader.ReadLine() ) != null )
                {
                    logs.Add( line );
                }
            }

            success = true;
        }
        catch ( IOException ex ) when ( i < numberOfRetries )
        {
            Local.Instance().Logger.Warn( ex, "Retrying reading logs from file path {0}, retry count {1} with dealy {2} ms.", filePath, i + 1,
                delayOnRetry );

            System.Threading.Thread.Sleep( delayOnRetry );
        }
    }

    GC.Collect();

    return logs;
}

【讨论】:

  • 虽然它确实很好地解决了这个问题,但不幸的是它并没有阻止它,这就是我想要找出的。感谢您的 sn-p 仍然。
  • 老实说我也会这样做,但我仍然很好奇它为什么会失败。
  • @FalcoGer @remy_rm 因为任何文件都可以从这段代码的范围之外访问,我们无法控制它。如果您希望文件只能供您的应用程序访问,请使用 .dat 文件。
猜你喜欢
  • 2022-01-22
  • 1970-01-01
  • 2012-01-29
  • 2016-05-31
  • 1970-01-01
  • 1970-01-01
  • 2013-04-05
  • 2018-04-07
  • 2014-01-30
相关资源
最近更新 更多