[英]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.