我有一些代码,当它执行时,它抛出IOException,说明

进程无法访问文件"filename",因为它正被

这意味着什么,我能做些什么?

推荐答案

原因是什么?

错误信息非常清楚:您试图访问一个文件,但由于另一个进程(甚至是同一个进程)正在对其进行操作(并且不允许任何共享),因此无法访问该文件.

调试

这可能很容易解决(也可能很难理解),这取决于您的特定场景.让我们看看吧.

Your process is the only one to access that file
You're sure the other process is your own process. If you know you open that file in another part of your program, then first of all you have to check that you properly close the file handle after each use. Here is an example of code with this bug:

var stream = new FileStream(path, FileAccess.Read);
var reader = new StreamReader(stream);
// Read data from this file, when I'm done I don't need it any more
File.Delete(path); // IOException: file is in use

幸运的是,FileStream实现了IDisposable,因此很容易将所有代码包装在using语句中:

using (var stream = File.Open("myfile.txt", FileMode.Open)) {
    // Use stream
}

// Here stream is not accessible and it has been closed (also if
// an exception is thrown and stack unrolled

这种模式还可以确保在出现异常时,文件不会保持打开状态(这可能是文件正在使用的原因:出现了问题,没有人关闭它;请参阅this post以获取示例).

如果一切看起来都很好(你确定你总是关闭你打开的每个文件,即使是在出现异常的情况下),并且你有多个工作线程,那么你有两个 Select :重新编写代码以序列化文件访问(不总是可行的,也不总是想要的),或者应用retry pattern.对于I/O操作来说,这是一种非常常见的模式:您try 执行某些操作,如果出现错误,您会等待并重试(例如,您是否问过自己,为什么Windows Shell会花一些时间通知您某个文件正在使用,无法删除?).在C#中,它很容易实现(另请参见关于database access95561/1207195">disk I/O、networkingdatabase access的更好示例).

private const int NumberOfRetries = 3;
private const int DelayOnRetry = 1000;

for (int i=1; i <= NumberOfRetries; ++i) {
    try {
        // Do stuff with file
        break; // When done we can break loop
    }
    catch (IOException e) when (i <= NumberOfRetries) {
        // You may check error code to filter some exceptions, not every error
        // can be recovered.
        Thread.Sleep(DelayOnRetry);
    }
}

请注意我们在StackOverflow上经常看到的一个常见错误:

var stream = File.Open(path, FileOpen.Read);
var content = File.ReadAllText(path);

在这种情况下,ReadAllText()将失败,因为该文件正在使用中(前面行中的File.Open()).事先打开文件不仅没有必要,而且也是错误的.这同样适用于所有File个函数,它们不会向您正在处理的文件返回handle:File.ReadAllText()File.WriteAllText()File.ReadAllLines()File.WriteAllLines()和其他函数(如File.AppendAllXyz()个函数)都将自行打开和关闭该文件.

Your process is not the only one to access that file
If your process is not the only one to access that file, then interaction can be harder. A retry pattern will help (if the file shouldn't be open by anyone else but it is, then you need a utility like Process Explorer to check who is doing what).

如何避免

如果适用,请始终使用using语句打开文件.如前一段所述,它将积极帮助您避免许多常见错误(参见how not to use it上的this post示例).

如果可能,try 确定谁拥有对特定文件的访问权限,并通过几种众所周知的方法集中访问.例如,如果您有一个程序读写的数据文件,那么您应该将所有I/O代码封装在单个类中.它将使调试变得更容易(因为您总是可以在那里放置断点并查看谁在做什么),而且它还将是多路访问的同步点(如果需要).

别忘了I/O操作总是会失败,一个常见的例子是:

if (File.Exists(path))
    File.Delete(path);

如果someoneFile.Exists()之后但在File.Delete()之前删除文件,那么它会在一个你可能错误地感到安全的地方抛出IOException.

只要有可能,应用一个retry pattern,如果你使用FileSystemWatcher,考虑推迟动作(因为你会得到通知,但是应用程序可能仍然完全与该文件一起工作).

Advanced scenarios
It's not always so easy, so you may need to share access with someone else. If, for example, you're reading from the beginning and writing to the end, you have at least two options.

1) 使用正确的同步功能共享相同的FileStream(因为it is not thread-safe).参见this篇和this篇文章中的例子.

2)使用FileShare枚举指示OS允许其他进程(或您自己进程的其他部分)并发访问同一文件.

using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.Read))
{
}

在这个例子中,我展示了如何打开一个文件进行写入和共享以进行读取;请注意,当读写重叠时,会导致数据未定义或无效.这是阅读时必须处理的情况.还要注意的是,这并不能保证对stream线程的访问是安全的,因此除非以某种方式同步访问,否则无法与多个线程共享该对象(请参阅前面的链接).还有其他共享选项,它们会带来更复杂的场景.更多详情请参考MSDN.

一般来说,N个进程可以一起读取同一个文件,但只有一个进程应该写入.在受控的情况下,您甚至可以启用并发写入,但这不能概括为本答案中的几个文本段落.

是否可以对另一个进程使用的文件进行unlock操作?这并不总是安全的,也不是那么容易的,但是的,it's possible.

.net相关问答推荐

EF核心类似功能T-SQL UPDATE FROM

向从 .NET 序列化的对象添加 Xml 属性

在 Rx 中,处理线程安全是否是消费者(IObserver)的责任?

查找 2 个已知值之间的字符串

如何在任务栏顶部全屏显示 Windows 窗体?

为什么要判断这个!= null?

共享 AssemblyInfo 用于跨解决方案的统一版本控制

为什么需要 XmlNamespaceManager?

为什么 C# 多维数组不实现 IEnumerable

将双精度转换为带有 N 个小数的字符串,点作为小数分隔符,并且没有千位分隔符

使用 .NET 中的代码更改桌面墙纸

如何在不丢失数据的情况下重命名 Entity Framework 5 Code First 迁移中的数据库列?

Await 运算符只能在 Async 方法中使用

如何在我的 C# 程序的面板中运行另一个应用程序?

.NET 事件 - 什么是对象发送者和 EventArgs e?

在 .NET 中计算目录大小的最佳方法是什么?

自创建数据库以来,支持ApplicationDbContext上下文的模型已更改

带有嵌套控件的设计模式

当它被抛出和捕获时,不要在那个异常处停止调试器

通过反射获取公共静态字段的值