来自注释的分辨率:
应用程序崩溃是由另一个问题引起的
我正在从 2 个不同的应用程序读取/写入文件,当文件被读取或写入时,它将始终被应用程序 A 或 B 锁定,并且它们都使用 FileShare.None
.
我的问题是,即使将阅读器包裹在try/catch周围,仍然会使使用行中的IOException使应用程序崩溃(写入器不会发生)。
我也把捕获作为catch (IOException ...
我相信除了使其更具可读性之外没有区别。
当文件被锁定并继续尝试直到文件可用时,忽略的正确方法是什么?
while (true)
{
try
{
using (FileStream stream = new FileStream("test_file.dat", FileMode.Open, FileAccess.Read, FileShare.None))
{
using (TextReader reader = new StreamReader(stream))
{
// bla bla bla does not matter
}
}
}
catch
{
// bla bla bla does not matter again
}
Thread.Sleep(500);
}
写
private bool WriteData(string data)
{
try
{
using (FileStream stream = new FileStream("test_file.dat", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
{
stream.SetLength(0);
using (TextWriter writer = new StreamWriter(stream))
{
writer.Write(data);
}
}
return true;
}
catch
{
return false;
}
}
请注意,当文件用于读取或写入的任何过程时,我不会向任何人授予共享权(写入者和读取器使用FileShare.None
),所以基本上我正在处理异常,直到文件可用,这是不工作的。
这是我们为此目的使用的代码。
/// <summary>
/// Executes the specified action. If the action results in a file sharing violation exception, the action will be
/// repeatedly retried after a short delay (which increases after every failed attempt).
/// </summary>
/// <param name="action">The action to be attempted and possibly retried.</param>
/// <param name="maximum">Maximum amount of time to keep retrying for. When expired, any sharing violation
/// exception will propagate to the caller of this method. Use null to retry indefinitely.</param>
/// <param name="onSharingVio">Action to execute when a sharing violation does occur (is called before the waiting).</param>
public static void WaitSharingVio(Action action, TimeSpan? maximum = null, Action onSharingVio = null)
{
WaitSharingVio<bool>(() => { action(); return true; }, maximum, onSharingVio);
}
/// <summary>
/// Executes the specified function. If the function results in a file sharing violation exception, the function will be
/// repeatedly retried after a short delay (which increases after every failed attempt).
/// </summary>
/// <param name="func">The function to be attempted and possibly retried.</param>
/// <param name="maximum">Maximum amount of time to keep retrying for. When expired, any sharing violation
/// exception will propagate to the caller of this method. Use null to retry indefinitely.</param>
/// <param name="onSharingVio">Action to execute when a sharing violation does occur (is called before the waiting).</param>
public static T WaitSharingVio<T>(Func<T> func, TimeSpan? maximum = null, Action onSharingVio = null)
{
var started = DateTime.UtcNow;
int sleep = 279;
while (true)
{
try
{
return func();
}
catch (IOException ex)
{
int hResult = 0;
try { hResult = (int) ex.GetType().GetProperty("HResult", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(ex, null); }
catch { }
if (hResult != -2147024864) // 0x80070020 ERROR_SHARING_VIOLATION
throw;
if (onSharingVio != null)
onSharingVio();
}
if (maximum != null)
{
int leftMs = (int) (maximum.Value - (DateTime.UtcNow - started)).TotalMilliseconds;
if (sleep > leftMs)
{
Thread.Sleep(leftMs);
return func(); // or throw the sharing vio exception
}
}
Thread.Sleep(sleep);
sleep = Math.Min((sleep * 3) >> 1, 10000);
}
}
使用示例:
Utilities.WaitSharingVio(
action: () =>
{
using (var f = File.Open(file, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
{
// ... blah, process the file
}
},
onSharingVio: () =>
{
Console.WriteLine("Sharing violation. Trying again soon...");
}
);
我曾经使用是否有全局命名的读取器/写入器锁中的信息执行此操作?
我想结果有点像在多个进程(而不是线程)访问资源的情况下工作的ReaderWriterLockSlim
。
可以使用互斥对象来保护共享资源,防止多个线程或进程同时访问。
您可以通过编写如下函数来检查文件锁定:
protected bool IsFileLocked(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
return true;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
}
Timwi的回答对我们有很大帮助(尽管在另一种情况下),但我发现如果你想从所有IOExceptions中获取HResult,还需要添加标志"BindingFlags.Public":
public static int GetHresult(this IOException ex)
{
return (int)ex.GetType().GetProperty("HResult", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(ex, null);
}
使用读写器锁定
正确的语法 捕获到
catch(Exception e)
while (true)
{
try
{
using (FileStream stream = new FileStream("test_file.dat", FileMode.Open, FileAccess.Read, FileShare.None))
{
using (TextReader reader = new StreamReader(stream))
{
// bla bla bla does not matter
}
}
}
catch(Exception e)
{
// bla bla bla does not matter again
}
Thread.Sleep(500);
}