4

私の WCF サービスではRange、チャンク ダウンロード用の HTTP ヘッダーをサポートするファイル ダウンロード機能を提供する必要があります。サービスのメソッドへの最初の呼び出しは、ディスク上のファイルからGetFile新しいMemoryMappedFileインスタンスを作成します。MMF が最初の要求によって作成され、その要求がまだ処理されているときにGetFile、サービスのメソッドへの 2 番目の呼び出しが既存の MMF を開き、ストリーミングされた応答をクライアントに返すと仮定しましょう。MMF を作成したスレッドによって MMF が破棄される (および MemoryMappedFile の破棄時にソース ファイルが閉じられる) とどうなりますか? 2 番目の呼び出しは、既に開いている ViewStream からすべてのコンテンツを正常に読み取る必要がありますか?

私は小さなテストを書いていましたが、MemoryMappedFile がOpenExistingメソッドによって開かれるまで、その有効期間が延長され、ソース ファイルが開かれたままになるようです。これは本当ですか、それとも落とし穴を見逃しましたか? MSDN でそのような場合のドキュメントが見つかりません。

更新: スレッドの競合をシミュレートするためにファイルの MapView を取得する前に、既存の MMF を開いた後に追加の Thread.Sleep 呼び出しを追加しました

private static readonly string mapName = "foo";
private static readonly string fileName = @"some big file";

static void Main(string[] args)
{
    var t1 = Task.Factory.StartNew(OpenMemoryMappedFile);
    var t2 = Task.Factory.StartNew(ReadMemoryMappedFile);

    Task.WaitAll(t1, t2);
}

private static void OpenMemoryMappedFile()
{
    var stream = File.OpenRead(fileName);
    using (var mmf = MemoryMappedFile.CreateFromFile(stream, mapName, 0, MemoryMappedFileAccess.Read, null, HandleInheritability.None, false))
    {
        Console.WriteLine("Memory mapped file created");
        Thread.Sleep(1000); // timeout for another thread to open existing MMF
    }

    Console.WriteLine("Memory mapped file disposed");
}

private static void ReadMemoryMappedFile()
{
    Thread.Sleep(100); //wait till MMF created
    var buffer = new byte[1024 * 1024]; //1MB chunk
    long totalLength = 0;
    using (var f = File.OpenRead(fileName))
    {
        totalLength = f.Length;
    }

    using (var mmf = MemoryMappedFile.OpenExisting(mapName, MemoryMappedFileRights.Read))
    {
        Console.WriteLine("Existing MMF opened successfully");
        Thread.Sleep(2000); //simulate threads race

        using (var viewStream = mmf.CreateViewStream(0, 0, MemoryMappedFileAccess.Read))
        {

            Console.WriteLine("View of file mapped successfully");
            File.Delete(Path.GetFileName(fileName));


            using (var fileStream = File.Open(Path.GetFileName(fileName), FileMode.CreateNew, FileAccess.Write))
            using (var writer = new BinaryWriter(fileStream))
            {
                int readBytes;
                do
                {
                    readBytes = viewStream.Read(buffer, 0, buffer.Length);
                    writer.Write(buffer, 0, readBytes);

                    Console.Write("{0:P}% of target file saved\r", fileStream.Length / (float)totalLength);
                    Thread.Sleep(10); //simulate network latency

                } while (readBytes > 0);
                Console.WriteLine();
                Console.WriteLine("File saved successfully");
            }
        }
    }
}
4

1 に答える 1

3

開いているビューは、他のファイル ハンドルが破棄されたり、ファイルが削除されたりしても、読み込んで引き離されることはありません。ビューは、明示的に閉じるまで有効なままです。同じことがファイル ハンドルにも当てはまります (ハンドルがまだ開いていて機能している間でもファイルを削除できます - これはあまり知られていない事実です)。

別のファイル ハンドルが閉じられた場合に閉じられたとします。次に、それを読み取るコードは、実行中にランダムなポイントで突然アクセス違反を生成し始めます。それは非常に不健全な設計になります。

ところで、スレッドはタイミングベースであるため、壊れています。しかし、実行できる再現ケースを作成しようとしているだけだと思います。

于 2013-08-19T22:50:15.890 に答える