IOException:进程无法访问文件的“文件path”,因为它正在被另一个进程使用
我有一些代码,当它执行时,它抛出一个IOException
,说
该进程无法访问文件的“文件名”,因为它正在被另一个进程使用
这是什么意思,我能做些什么呢?
原因是什么?
错误信息非常明确:你正试图访问一个文件,而且它不可访问,因为另一个进程(甚至是同一个进程)正在做一些事情(而且它不允许任何共享)。
debugging
根据您的具体情况,解决这个问题可能相当容易(或者很难理解)。 让我们看看一些。
您的进程是唯一访问该文件的人
你确定另一个过程是你自己的过程。 如果您知道在程序的另一部分中打开该文件,则首先必须检查每次使用后是否正确closures文件句柄。 下面是这个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
这种模式还可以确保在出现exception的情况下文件不会被打开(这可能是文件被使用的原因:出错了,没有人closures它;查看这个post的例子)。
如果一切看起来都很好(即使在例外的情况下,你也确实closures了每个文件),并且你有多个工作线程,那么你有两个select:重写你的代码以序列化文件访问(不总是可行的,并不总是想要的)或应用重试模式 。 这是一种非常常见的I / O操作模式:您尝试做某些事情,如果出现错误,请等待并重试(例如,您是否问自己为什么,例如,Windows Shell需要一些时间通知您文件正在使用不能删除?)。 在C#中,实现起来相当容易(另请参阅有关磁盘I / O , networking和数据库访问的更好示例)。
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.ReadAllText()
, File.WriteAllText()
, File.ReadAllLines()
, File.WriteAllLines()
等File
函数也是如此。 File.AppendAllXyz()
函数)将自行打开和closures文件。
您的进程不是唯一一个访问该文件的人
如果你的进程不是唯一访问该文件的进程,那么交互可能会更困难。 重试模式将有所帮助(如果该文件不应该由其他人打开,那么您需要一个像Process Explorer这样的实用程序来检查是谁在做什么 )。
避免的方法
适用时,请始终使用使用语句来打开文件。 正如前一段所述,它会积极地帮助你避免许多常见的错误(关于如何不使用它的例子见这篇文章 )。
如果可能的话,尝试确定谁拥有对特定文件的访问权限,并通过一些众所周知的方法集中访问权限。 例如,如果你有一个数据文件在你的程序读写,那么你应该把所有的I / O代码放在一个类中。 它会使debugging变得更容易(因为你总是可以在那里放置一个断点,看看谁在做什么),同时也是一个同步点(如果需要的话)用于多路访问。
不要忘记I / O操作总是会失败,一个常见的例子是:
if (File.Exists(path)) File.Delete(path);
如果有人在File.Exists()
但在File.Delete()
之前删除文件,那么它会在可能错误感觉安全的地方抛出IOException
。
只要有可能,应用一个重试模式 ,如果你正在使用FileSystemWatcher
,考虑推迟行动(因为你会得到通知,但应用程序可能仍然是专门用该文件)。
高级场景
这并不总是那么容易,所以你可能需要与其他人共享访问权限。 例如,如果你从一开始就阅读并写作到最后,你至less有两个select。
1)共享相同的FileStream
与适当的同步function(因为它不是线程安全的 )。 看到这个和这个职位的例子。
2)使用FileShare
枚举指示操作系统允许其他进程(或您自己的进程的其他部分)同时访问同一个文件。
using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.Read)) { }
在这个例子中,我展示了如何打开一个文件来写作和分享阅读; 请注意,在读写重叠时,会导致未定义或无效的数据。 这是阅读时必须处理的情况。 另外请注意,这不会使线程安全的访问stream
,所以这个对象不能与多个线程共享,除非以某种方式同步访问(见前面的链接)。 其他共享选项是可用的,他们打开更复杂的情况。 有关更多详细信息,请参阅MSDN 。
一般而言, N个进程可以从同一个文件一起读取,但是只有一个应该写入,在一个受控的场景中,甚至可以启用并发的写入,但是在这个答案中的几个文本段落中,这不能被概括。
是否有可能解锁另一个进程使用的文件? 这并不总是安全的,不是那么容易,但是, 是可能的 。
我有以下情况导致相同的错误:
- 上传文件到服务器
- 然后在上传之后摆脱旧文件
大多数文件的大小很小,但是有一些是大的,所以试图删除那些导致无法访问的文件错误。
然而,find这个解决scheme并不容易,就像等待 “完成任务”一样简单:
using (var wc = new WebClient()) { var tskResult = wc.UploadFileTaskAsync(_address, _fileName); tskResult.Wait(); }