流关闭后文件仍然被锁定

4

我有一些代码,试图从一个可能加密的文件中读取数据。如果它是加密的,那么在实际数据之前有几个字节的信息头。访问文件的逻辑如下:

bool encrypted = IsEncryptedFile(fileName);
Stream s = null;

if (encrypted)
{
    s = new EncryptedStreamReader(fileName);
}
else
{
    s = new StreamReader(fileName);
}

// Read from s

在极少数情况下(比如10000个文件中的一个),当尝试创建流读取器时,会出现错误“由于另一个进程正在使用该文件xxx,因此无法访问文件”。IsEncryptedFile方法打开文件以读取标头(如果存在),没有其他代码访问该文件。该方法总是关闭文件(它在using语句中打开),并且它总是成功的。
我假设关闭.NET流不保证底层操作系统句柄被关闭,并添加了等待和重试的代码。这减少了错误发生的频率,但仍然偶尔会出现。
我的问题是:
我的假设正确吗?即关闭然后立即打开文件的代码可能会收到此错误,因为Windows仍在释放它,即使.NET Stream.Close方法调用已返回(或using块已退出)?
有没有比扩展重试间隔更好的解决方法?
编辑
IsEncryptedFile做了这件事
private bool IsEncryptedFile(string fileName)
{
    using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
    {
        //Read header from fileStream
        //Returns true if encrypted else false
    }
}

由于显而易见的原因,我不会包括实际逻辑,但无论如何这都是无关紧要的,因为你需要知道的是流被 始终 关闭 (使用 语句)。

这不是多线程问题。我的代码中也没有其他部分访问文件。该方法每个文件只调用一次。'从s读取'中发生了什么并不重要,因为在那时,文件已经被打开(9999次中的OK - 10000中的1次)或未打开(糟糕 - 10000中的1次)。


5
你确定问题不是由其他进程访问你的文件引起的副作用,比如杀毒软件、碎片整理工具等吗? - Roy Dictus
可能IsEncryptedFile函数与某些IDisposable类一起使用,并且没有进行释放。请参考:using C#语句 http://msdn.microsoft.com/zh-cn/library/yh598w02.aspx - Alex F
1
你能发布IsEncryptedFile的源代码吗? - Original10
“// 读取 s 的代码是什么样子的?” - Mike Perrenoud
s 也会被关闭和处理吗? - rene
显示剩余4条评论
1个回答

1

确保使用usingtry finally子句正确处理流的释放。

此外,您可以使用FileStream设置文件访问选项。

Ex.:

bool encrypted = IsEncryptedFile(fileName);
Stream s = null;
try
{
    if (encrypted)
    {
        s = new EncryptedStreamReader(fileName);
    }
    else
    {
        s = new FileStream(fileName, FileMode.Open, FileAccess.Read);
    }

    // Read from s
}
finally
{
    if (s != null)
        s.Close();
}

检查所有打开流的方法(例如 IsEncryptedFile)以确保所有流都被正确关闭。

1
我会将最后一行的s.Close()改为s.Dispose() - Luis Filipe
根据文档,dispose的作用是: 释放System.IO.TextReader对象使用的所有资源。 而Close的作用是: 关闭System.IO.TextReader并释放与TextReader相关联的任何系统资源。 - rkawano
Dispose调用'Close'。如果类实现了IDisposable,应该使用Dispose()。如果实现者更改了内部逻辑,您的代码将不会中断。不要尝试通过“粘贴”它实现的任何代码来猜测dispose的作用。 - Luis Filipe
我粘贴了StreamReader框架的文档。正如你可以在这里看到的那样,有很多关于这两种方法的讨论:这里 这里这里 - rkawano
在实际代码中,s已被处理,但这不是重点。问题在于IO异常是在打开s之前抛出的。 - user2729292

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接