与MemoryMappedFile的连接不可靠

时间:2013-02-14 16:34:45

标签: .net .net-4.0 memory-mapped-files

我有两个.NET 4.0 WinForms应用程序(主要)从共享的MemoryMappedFiles读取地理数据。最终用户可以自由启动其中一个应用程序,也可以同时运行这两个应用程序。第一个打开的应用程序创建了命名的MemoryMapFile-s,第二个打开已经存在的应用程序。 但是,打开现有的名为MemoryMappedFile似乎不可靠。它大约80%的情况下工作,但大约20%的情况下它失败了FileNotFoundException。 症状不能安全再现,当它失败并成功时似乎是纯粹的运气。

以下是两个应用程序用于获取MemoryMappedFiles的代码:

private static MemoryMappedFile GetMemoryMappedFile(string filePath)
{
    string mapName = filePath; // I have also tried here @"Global\myfile", no difference
    MemoryMappedFile mmf = null;
    try 
    { 
        // When the first app executes this step, it always succeeds.
        // When the second app comes here, it fails as it should.
        mmf = MemoryMappedFile.CreateFromFile(filePath, FileMode.OpenOrCreate,
                              mapName, HundredMB, MemoryMappedFileAccess.ReadWrite); 
    }
    catch (IOException)
    {
        try 
        { 
            // Opening the already existing named MemoryMappedFile by the SECOND app.
            // This line fails about 20% of the time.
            mmf = MemoryMappedFile.OpenExisting(mapName, 
                                   MemoryMappedFileRights.ReadWrite); 
        } 
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("Yet again, could not open MMF. Life sux.");
        }
    }
    return mmf;
}

4 个答案:

答案 0 :(得分:3)

你所描述的似乎是一个非常可能的案例。你所拥有的是一种竞争条件。例如,在CreateFromFile上抛出异常的代码告诉您该文件存在。然后,另一个进程在此代码到达OpenExisting之前关闭文件。然后调用OpenExisting并因为文件不再存在而失败。这是您需要为其编写代码的有效案例。

答案 1 :(得分:3)

 string mapName = filePath;

这看起来非常可疑。 filePath 字符串应始终引用文件的绝对路径名。像“c:\ foo \ bar \ baz.bin”一样,从不使用相对文件名,例如“baz.bin”。像“Global \ myfile”这样的名称不是路径名,而是内存映射名。这是另一个进程用于打开映射的内容,它不知道或不关心充当映射的后备存储的实际文件。

这种失败的一种非常常见的方式是当您的程序的工作目录(Environment.CurrentDirectory)未设置在您希望设置的位置时。除了突然看到CreateFromFile()调用失败之外,它有一个改变的诀窍而不会意识到。

因此,请选择一个非常适合您应用的唯一地图名称来解决您的问题。以及使用完整路径名选择的文件。通常存储在AppData中,您可以在没有UAC提升的情况下具有写入权限的文件夹。

答案 2 :(得分:2)

如何翻转逻辑?由于MMF的创建应该是一次性的事情,但现有的开放应该是更常见的情况,也许这会有用吗?

private static MemoryMappedFile GetMemoryMappedFile(string filePath)
{
    var mapName = filePath; // I have also tried here @"Global\myfile", no difference
    MemoryMappedFile mmf = null;

    try
    {
        // When the first app executes this step, it fails as it should.
        // Opening the already existing named MemoryMappedFile by the SECOND app.
        mmf = MemoryMappedFile.OpenExisting(mapName, MemoryMappedFileRights.ReadWrite);
    }
    catch (FileNotFoundException)
    {
        try
        {
            // When the first app executes this step, it always succeeds.
            mmf = MemoryMappedFile.CreateFromFile(
                filePath,
                FileMode.OpenOrCreate,
                mapName,
                HundredMB,
                MemoryMappedFileAccess.ReadWrite);
        }
        catch (IOException ex)
        {
            Console.Error.WriteLine("Yet again, could not open MMF. Life sux: " + ex);
        }
    }

    return mmf;
}

答案 3 :(得分:1)

我是这个问题的作者,而且我是个傻瓜。 MMF按照预期工作。失败是由第二种方法(很久以前由我编写)引起的,我创建了MMF-s,但我在那里应用了不同的MMF命名约定。问题中的代码失败(正确),因为某些MMF是在我的第二个方法中以错误的名称创建的。

我不会删除这个问题,因为仍有一些有价值的答案可能有助于后来的读者。非常感谢所有花时间回答的人。