4

In my WCF service I need to provide functionality of files downloading with supporting of Range HTTP header for chunked downloading. The first call to GetFile method of service creates new MemoryMappedFile instance from file on disk. Let's assume that in time when MMF created by the first request and that request still processing, the second call to GetFile method of service opening existing MMF and returning streamed response to client. What happens if MMF will be disposed (and source file closed on MemoryMappedFile disposing) by thread which create it? Should second call successfully read all content from already opened ViewStream or no?

I had wrote small test and seems that till MemoryMappedFile opened by OpenExisting method, it's lifetime extended and source file keeps opened. Is this true, or I missed some pitfall? I can't find any documentation for such case in MSDN.

Update: added additional Thread.Sleep call after existing MMF opened before obtaining MapView of file to simulate threads race

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 回答