2

我有两个 .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

4 回答 4

3

你所描述的似乎是一个完全可能的情况。你所拥有的是一个竞争条件。例如,引发异常的代码CreateFromFile告诉您文件存在。然后另一个进程在此代码到达OpenExisting. OpenExisting然后调用并失败,因为该文件不再存在。这是您需要为其编写代码的有效案例。

于 2013-02-14T16:40:22.480 回答
3
 string mapName = filePath;

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

这将失败的一种非常常见的方式是当您的程序的工作目录(Environment.CurrentDirectory)未设置在您希望设置的位置时。除了突然看到 CreateFromFile() 调用失败之外,它具有在您没有意识到的情况下进行更改的诀窍。

因此,通过选择一个高度特定于您的应用程序的唯一地图名称来解决您的问题。以及您选择的具有完整路径名的文件。通常存储在 AppData 中,这是一个无需 UAC 提升即可写入访问的文件夹。

于 2013-02-14T19:29:41.593 回答
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;
}
于 2013-02-14T19:09:17.290 回答
1

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

我不会删除这个问题,因为仍然有有价值的答案可能对后来的读者有所帮助。非常感谢大家,谁花时间回答。

于 2013-02-15T10:43:21.303 回答