正在使用由MemoryMappedFile.OpenExisting方法线程安全打开的MemoryMapedFile实例



在我的WCF服务中,我需要提供文件下载功能,并支持用于分块下载的Range HTTP标头。对服务的GetFile方法的第一个调用从磁盘上的文件创建新的MemoryMappedFile实例。让我们假设,当第一个请求创建的MMF和该请求仍在处理时,第二个调用GetFile的服务方法打开现有的MMF并向客户端返回流式响应。如果MMF将由创建它的线程处理(并且源文件在MemoryMappedFile处理上关闭),会发生什么?第二次调用是否应成功读取已打开ViewStream中的所有内容?

我写了一个小测试,似乎直到用OpenExisting方法打开MemoryMappedFile,它的寿命才延长,并且源文件一直处于打开状态。这是真的吗,还是我错过了一些陷阱?我在MSDN中找不到任何关于这种情况的文档。

更新:在现有MMF打开后添加额外的Thread.Sleep调用,然后获取文件的MapView以模拟线程竞争

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 savedr", fileStream.Length / (float)totalLength);
                    Thread.Sleep(10); //simulate network latency
                } while (readBytes > 0);
                Console.WriteLine();
                Console.WriteLine("File saved successfully");
            }
        }
    }
}

如果其他文件句柄被释放或文件被删除,则打开的视图不会在您读取它时被拉开。视图在显式关闭之前保持有效。文件句柄也是如此(您可以在句柄仍处于打开和工作状态时删除文件——这是一个鲜为人知的事实)。

假设如果关闭了另一个文件句柄,则它已关闭。然后你的代码读取它会突然开始在执行过程中的随机点产生访问违规。这将是一个非常不合理的设计。

顺便说一句,你的线程是基于时间的,因此会中断。但我认为你只是想创造一个可以执行的repo案例。

最新更新