MaxMind.Db.Reader クラスを使用して GeoLite2-City.mmdb ファイルにアクセスしていますが、リーダーで dispose を呼び出した後、ファイルに対して特定のアクションを実行するとファイル アクセスの問題が発生します。
この問題を再現する最も簡単な方法は、MaxMind.Db.Benchmark プロジェクトに似たコンソール アプリケーションを用意することです。これには、私の場合は _cityReader と呼ばれる Reader クラスの静的インスタンスがあります。
ここで、_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");
私が見つけたのは、リフレクションを使用して_cityReader._stream.ValueプロパティのDisposeを呼び出すと、このシナリオでは、_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 オブジェクトで同様の問題を経験した人はいますか?
私が間違っていること、または私がすべきことは他にありますか? ガベージコレクションを強制するのは少し汚い気がするので、できれば避けたいと思います。
ありがとう、
フィル