[英]C# .NET BackgroundWorker 6 FileSystemWatcher The process cannot access the file "PATH" because it is being used by another process
[英]FileStream and a FileSystemWatcher in C#, Weird Issue “process cannot access the file”
我有这个复杂的代码库,它正在侦听某个文件夹上的 FileCreated 事件。 创建文件后(还包括将文件移动到该文件夹),我想读入该文件并对其进行处理。 它适用于第一个文件,但在所有其他尝试之后抛出异常。 在调试模式(使用 VisualStudio)中,错误会被抛出,但如果我只是点击“继续”......它就会工作(没有错误)。
我已经发布了简化的代码,它演示了这个问题。
例如,您启动应用程序,单击“开始”按钮,然后“创建新的文本文件”
输出是:
Working
如果您随后以完全相同的方式创建第二个文件,则输出为:
Broken: The process cannot access the file 'C:\TestFolder\New Text Document (2).txt' because it is being used by another process.
Working, after breaking
查看我的代码后,您将看到上面的打印输出集意味着首先抛出了“无法访问文件”异常,但是在 catch 语句中执行相同的调用突然起作用了。
这对我来说没有任何意义,因为该文件显然没有被其他任何东西使用(我刚刚创建了它)……无论如何它一秒钟后就可以工作了……
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" >
<StackPanel>
<Button Click="Button_Click" Content="Start"/>
</StackPanel>
</Window>
背后的代码:
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
test();
}
String Folder = @"C:\TestFolder";
private void test()
{
FileSystemWatcher watch = new FileSystemWatcher(Folder);
watch.Created += new FileSystemEventHandler(FileCreated);
watch.EnableRaisingEvents = true;
Process.Start(Folder);
}
private void FileCreated(object sender, FileSystemEventArgs fsEvent)
{
if (File.Exists(fsEvent.FullPath))
{
// Thread.Sleep(1000);// Sleeping for 1 second seems to prevent the error from happening...?
// If i am debugging, and pause on exceptions... then it also suddenly works (similar to the Sleep above)
try
{
FileStream fs = new FileStream(fsEvent.FullPath, FileMode.Open);
Console.WriteLine("Working");
fs.Close();
}
catch (IOException ex)
{
Console.WriteLine("Broken: " + ex.Message);
try
{
FileStream fs = new FileStream(fsEvent.FullPath, FileMode.Open);
Console.WriteLine("Working, after breaking");
fs.Close();
}
catch(IOException ex2)
{
FileStream fs = new FileStream(fsEvent.FullPath, FileMode.Open);
Console.WriteLine("really broken: " + ex2.Message);
fs.Close();
}
}
}
}
}
}
我已经看到了自 .NET 1.0 以来您描述的行为,并且从未费心找出它发生的原因。 在您调用 close 和 dispose 之后,似乎操作系统或 .NET 有时(?)会在短时间内锁定文件。
我做了一个变通方法 - 如果你喜欢,或者 hack - 已经证明对我们来说非常强大。 我们每天在服务器场中处理数百万个文件,文件观察者检测到的所有文件都通过此方法,然后再将其移交给进一步处理。
它的作用是在文件上放置一个排他锁。 如果失败,它会选择等待 10 秒让文件关闭,然后再放弃。
public static bool IsFileClosed(string filepath, bool wait)
{
bool fileClosed = false;
int retries = 20;
const int delay = 500; // Max time spent here = retries*delay milliseconds
if (!File.Exists(filepath))
return false;
do
{
try
{
// Attempts to open then close the file in RW mode, denying other users to place any locks.
FileStream fs = File.Open(filepath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
fs.Close();
fileClosed = true; // success
}
catch (IOException) {}
if (!wait) break;
retries --;
if (!fileClosed)
Thread.Sleep( delay );
}
while (!fileClosed && retries > 0);
return fileClosed;
}
最有可能发生的情况是FileCreated
事件被引发并尝试在文件完全写入磁盘之前处理文件。
有关避免此问题的技术,请参阅等待文件完全写入。
尝试禁用任何防病毒/反恶意软件。 默认情况下,大多数配置为在创建时扫描文件。
Ty 到 EventHorizon 上面的代码,很好地满足了我的需求,并且似乎捕捉到了 Brendan 共享链接中的本质。
将其稍微修改为异步返回元组(既用于在 do/while 结束时取回文件锁定状态,也用于传递 ms 以查看发生了哪种退出)。 我唯一不舒服的是,我不确定为什么会这样,即使将延迟降低到 2 毫秒,我仍然无法触发 catch IOException 条件(超过一个 do 循环的证据) ) 所以我对 IO 故障情况没有任何直接可见性,其他人可能有更大的文件,他们可以通过以下方式验证:
public async Task<(bool, int)> IsFileClosed(string filepath)
{
bool fileClosed = false;
int baseretries = 40;
int retries = 40;
const int delay = 250;
if (!File.Exists(filepath))
return (false,0);
Task<bool> FileCheck = Task.Run(() =>
{
do
{
try
{
FileStream fs = File.Open(filepath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
fs.Close();
fileClosed = true;
}
catch (IOException) { }
retries--;
if (!fileClosed)
Thread.Sleep(delay);
}
while (!fileClosed && retries > 0);
return fileClosed;
});
fileClosed = await FileCheck;
return (fileClosed, (baseretries - retries) * delay);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.