我正在使用 MaxMind.Db.Reader 类访问 GeoLite2-City.mmdb 文件,并且在对阅读器调用 dispose 后对文件执行某些操作时遇到文件访问问题。
重现此问题的最简单方法是拥有一个控制台应用程序,类似于 MaxMind.Db.Benchmark 项目,它有一个 Reader 类的静态实例,在我的例子中称为 _cityReader。
现在,如果您将 _cityReader 变量设置为 Reader 的新实例,然后对其调用 dispose(我也将其设置为 null),然后移动文件(移动工作正常),然后在文件的新位置调用 delete,您将获得 UnauthorizedAccessException在删除操作...
_cityReader = new Reader(@"C:\temp\MaxMind\Active\GeoLite2-City.mmdb", FileAccessMode.MemoryMapped);
_cityReader.Dispose();
_cityReader = null;
File.Move(@"C:\temp\MaxMind\Active\GeoLite2-City.mmdb", @"C:\temp\MaxMind\Active\GeoLite2-CityMoved.mmdb");
File.Delete(@"C:\temp\MaxMind\Active\GeoLite2-CityMoved.mmdb");
我发现,如果我使用反射调用 Dispose _cityReader._stream.Value 属性,那么在这种情况下,在调用 _cityReader 变量本身的 dispose 之前,文件将被删除......
_cityReader = new Reader(@"C:\temp\MaxMind\Active\GeoLite2-City.mmdb", FileAccessMode.MemoryMapped);
FieldInfo field = typeof(Reader).GetField("_stream", BindingFlags.NonPublic | BindingFlags.Instance);
ThreadLocal<Stream> fieldValue = (ThreadLocal<Stream>)field.GetValue(_cityReader);
fieldValue.Value.Dispose();
_cityReader.Dispose();
_cityReader = null;
File.Move(@"C:\temp\MaxMind\Active\GeoLite2-City.mmdb", @"C:\temp\MaxMind\Active\GeoLite2-CityMoved.mmdb");
File.Delete(@"C:\temp\MaxMind\Active\GeoLite2-CityMoved.mmdb");
但是,除此之外,我实际上将静态读取器对象处理在与创建它的线程不同的线程上,并发现即使在这种情况下使用反射进行此操作也意味着我仍然会得到 UnauthorizedAccessException。在这种情况下,我发现在处理 _cityReader 变量并将其设置为 null 后,我需要强制进行垃圾收集。为了重现这个,我在控制台应用程序中有一个像这样的静态方法......
private static void DisposeReaderAndSwitchFiles()
{
_cityReader.Dispose();
_cityReader = null;
GC.Collect();
// Try switching files
File.Move(@"C:\temp\MaxMind\Active\GeoLite2-City.mmdb", @"C:\temp\MaxMind\Active\GeoLite2-CityMoved.mmdb");
File.Delete(@"C:\temp\MaxMind\Active\GeoLite2-CityMoved.mmdb");
}
然后在控制台应用程序的主要方法中我有这个......
_cityReader = new Reader(@"C:\temp\MaxMind\Active\GeoLite2-City.mmdb", FileAccessMode.MemoryMapped);
Thread t = new Thread(DisposeReaderAndSwitchFiles);
t.Start();
t.Join();
Console.WriteLine("Press Any Key To Continue...");
Console.ReadKey();
所以该代码示例将起作用,但如果我从 DisposeReaderAndSwitchFiles 方法中删除 GC.Collect() 调用,我会得到 UnauthorizedAccessException。
MaxMind.Db.Reader 对象是否有其他人遇到过类似问题?
有什么我做错了还是我应该做的其他事情?强制垃圾收集感觉有点脏,如果可能的话,我宁愿避免它。
谢谢,
菲尔