首页 > 解决方案 > System.IO.Compression.ZipArchive 在处理后保持文件锁定?

问题描述

我有一个从多个来源获取数据并将它们写入 ZIP 文件的类。我已经对课程进行了基准测试,以检查 usingCompressionLevel.Optimal是否会比CompressionLevel.Fastest. CompressionLevel但是每次我运行基准测试时,基准测试都会在不同的迭代和不同的值中引发异常。

我开始逐步删除添加文件内容的方法,直到我最终得到下面的代码(在 for 循环内),除了创建一个空的 zip 文件并删除它之外,它基本上什么都不做。

简化代码:

var o = @"e:\test.zip";
var result = new FileInfo(o);
for (var i = 0; i < 1_000_000; i++)
{
    // Alternate approach
    // using(var archive = ZipFile.Open(o, ZipArchiveMode.Create))
    using (var archive = new ZipArchive(result.OpenWrite(), ZipArchiveMode.Create, false, Encoding.UTF8))
    {
    }

    result.Delete();
}

该循环在我的 PC 上运行了大约 100 到 15k 次迭代,然后在尝试删除文件时抛出 IOException,说明文件 ( result) 已锁定。

所以......我错过了一些关于如何使用的东西System.IO.Compression.ZipArchive吗?ZipArchive 没有关闭方法,使用应该处理/关闭存档...我尝试了不同的 .NET 版本 4.6、4.6.1、4.7 和 4.7.2。

编辑 1:不是基准代码的
一部分result.Delete()

编辑 2:
还尝试Thread.Sleep(5/10/20)在 using 块之后玩弄(因此result.Delete()要检查锁定是否仍然存在),但在 20 毫秒内,文件仍会在某些时候被锁定。没有尝试比 20ms 更高的值。

编辑 3:
无法在家中重现问题。在工作中尝试了十几次,循环从未达到 20k 次迭代。在这里尝试过一次,它完成了。

编辑4:
jdweng(见评论)是对的。谢谢!它在某种程度上与我在本地硬盘上的“e:”分区有关。相同的代码在本地 ssd 和网络共享上的“c:”分区上运行良好。

标签: c#zipsystem.io.compression

解决方案


根据我的经验,当流的 dispose 方法返回时,文件可能不会始终如一地解锁。我最好的猜测是,这是由于文件系统异步执行了一些操作。我找到的最佳解决方案是多次重试删除操作。即是这样的:

    public static void DeleteRetrying(this FileInfo self, int delayMs = 100, int numberOfAttempts = 3)
    {
        for (int i = 0; i < numberOfAttempts-1; i++)
        {
            try
            {
                self.Delete();
            }
            catch (IOException)
            {
                // Consider making the method async and
                // replace this with Task.Delay
                Thread.Sleep(delayMs);
            }
        }
        // Final attempt, let the exception propagate
        self.Delete();
    }

这不是一个理想的解决方案,如果有人可以提供更好的解决方案,我会很高兴。但它可能足以测试未删除文件的影响在哪里可以管理。


推荐阅读