C# 什么';等待释放文件锁的正确模式是什么?
我需要打开一个文件,但如果它当前不可用,我需要等到它准备好。最好的方法是什么 场景 我使用文件作为应用程序数据的持久缓存机制。这些数据需要经常读取和反序列化(只写入一次,偶尔删除)。我有一个在单独线程上运行的清理过程,该线程确定哪些文件不再需要并删除它们。打开和读取文件可能会同时发生(很少,但可能会发生),我希望进程等待并再次尝试读取数据 谢谢 与所有“最佳方法是什么”问题一样,这一问题取决于您的需要。一些容易想到的选项:C# 什么';等待释放文件锁的正确模式是什么?,c#,.net,locking,C#,.net,Locking,我需要打开一个文件,但如果它当前不可用,我需要等到它准备好。最好的方法是什么 场景 我使用文件作为应用程序数据的持久缓存机制。这些数据需要经常读取和反序列化(只写入一次,偶尔删除)。我有一个在单独线程上运行的清理过程,该线程确定哪些文件不再需要并删除它们。打开和读取文件可能会同时发生(很少,但可能会发生),我希望进程等待并再次尝试读取数据 谢谢 与所有“最佳方法是什么”问题一样,这一问题取决于您的需要。一些容易想到的选项: 中止尝试 循环,直到文件解锁 询问用户对此做些什么 您选择哪一个取决于您
您选择哪一个取决于您如何处理它。取决于谁控制文件。如果应用程序的一部分需要等到应用程序的另一部分完成文件准备,则可以使用
ManualResetEvent
。也就是说,在启动时,程序会创建一个新事件:
public ManualResetEvent FileEvent=新的ManualResetEvent(false)代码>
现在,程序中等待文件的部分有以下代码:
FileEvent.WaitOne()代码>
创建文件的程序部分在文件准备就绪时执行以下操作:
FileEvent.Set()代码>
如果您的应用程序必须等待另一个您无法控制的应用程序正在使用的文件,那么唯一真正的解决方案就是不断尝试打开该文件
FileStream f = null;
while (f == null)
{
try
{
f = new FileStream(...);
}
catch (IOException)
{
// wait a bit and try again
Thread.Sleep(5000);
}
}
当然,您可能不希望无条件地捕获IOException
。您可能希望捕获您知道如何处理的特定异常(例如,如果遇到DirectoryNotFoundException
,则不希望重试)。I/O函数记录了期望抛出的异常以及在什么情况下抛出的异常。我不太喜欢try/catch IOException,因为:
出现异常的原因未知
我不喜欢“预期”异常,因为我经常在异常中断时运行
您可以通过调用CreateFile并在它最终返回句柄时/如果它最终返回句柄,则返回一个流来实现这一点,而不会出现异常:
public static System.IO.Stream WaitForExclusiveFileAccess(string filePath, int timeout)
{
IntPtr fHandle;
int errorCode;
DateTime start = DateTime.Now;
while(true)
{
fHandle = CreateFile(filePath, EFileAccess.GenericRead | EFileAccess.GenericWrite, EFileShare.None, IntPtr.Zero,
ECreationDisposition.OpenExisting, EFileAttributes.Normal, IntPtr.Zero);
if (fHandle != IntPtr.Zero && fHandle.ToInt64() != -1L)
return new System.IO.FileStream(fHandle, System.IO.FileAccess.ReadWrite, true);
errorCode = Marshal.GetLastWin32Error();
if (errorCode != ERROR_SHARING_VIOLATION)
break;
if (timeout >= 0 && (DateTime.Now - start).TotalMilliseconds > timeout)
break;
System.Threading.Thread.Sleep(100);
}
throw new System.IO.IOException(new System.ComponentModel.Win32Exception(errorCode).Message, errorCode);
}
#region Win32
const int ERROR_SHARING_VIOLATION = 32;
[Flags]
enum EFileAccess : uint
{
GenericRead = 0x80000000,
GenericWrite = 0x40000000
}
[Flags]
enum EFileShare : uint
{
None = 0x00000000,
}
enum ECreationDisposition : uint
{
OpenExisting = 3,
}
[Flags]
enum EFileAttributes : uint
{
Normal = 0x00000080,
}
[DllImport("kernel32.dll", EntryPoint = "CreateFileW", SetLastError = true, CharSet = CharSet.Unicode)]
static extern IntPtr CreateFile(
string lpFileName,
EFileAccess dwDesiredAccess,
EFileShare dwShareMode,
IntPtr lpSecurityAttributes,
ECreationDisposition dwCreationDisposition,
EFileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile);
#endregion
csharptest.net方法的更通用版本可能如下所示(同样,使用SafeFileHandle
并删除超时时引发的异常,您可以在以下位置获取枚举值):
1) 不是等待锁可用的非常有效的方法。3) 虽然可能是正确的解决方案,但它不是一个编程解决方案。2)我怀疑,这是弥迦试图改进的默认解决方案。我要指出,最初的问题与现在的形式完全不同。这一切都是在一个过程中发生的吗?是的。它实际上是在Windows7手机(Silverlight)上使用的。
public static FileStream WaitForFileAccess(string filePath, FileMode fileMode, FileAccess access, FileShare share, TimeSpan timeout)
{
int errorCode;
DateTime start = DateTime.Now;
while (true)
{
SafeFileHandle fileHandle = CreateFile(filePath, ConvertFileAccess(access), ConvertFileShare(share), IntPtr.Zero,
ConvertFileMode(fileMode), EFileAttributes.Normal, IntPtr.Zero);
if (!fileHandle.IsInvalid)
{
return new FileStream(fileHandle, access);
}
errorCode = Marshal.GetLastWin32Error();
if (errorCode != ERROR_SHARING_VIOLATION)
{
break;
}
if ((DateTime.Now - start) > timeout)
{
return null; // timeout isn't an exception
}
Thread.Sleep(100);
}
throw new IOException(new Win32Exception(errorCode).Message, errorCode);
}
private static EFileAccess ConvertFileAccess(FileAccess access)
{
return access == FileAccess.ReadWrite ? EFileAccess.GenericRead | EFileAccess.GenericWrite : access == FileAccess.Read ? EFileAccess.GenericRead : EFileAccess.GenericWrite;
}
private static EFileShare ConvertFileShare(FileShare share)
{
return (EFileShare) ((uint) share);
}
private static ECreationDisposition ConvertFileMode(FileMode mode)
{
return mode == FileMode.Open ? ECreationDisposition.OpenExisting : mode == FileMode.OpenOrCreate ? ECreationDisposition.OpenAlways : (ECreationDisposition) (uint) mode;
}
[DllImport("kernel32.dll", EntryPoint = "CreateFileW", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern SafeFileHandle CreateFile(
string lpFileName,
EFileAccess dwDesiredAccess,
EFileShare dwShareMode,
IntPtr lpSecurityAttributes,
ECreationDisposition dwCreationDisposition,
EFileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile);