【问题标题】:Clearing contents of memory mapped file in C#在 C# 中清除内存映射文件的内容
【发布时间】:2014-01-04 22:59:34
【问题描述】:

我正在使用 MemoryMappedFile 在 2 个程序之间进行通信。程序“A”创建 mmf 并在计时器上读取它的内容。程序“B”将 xml 数据写入计时器上的 mmf。我的内存映射工作正常,但我遇到了一个问题,即 XML 数据的上一次迭代比当前数据更长,并且旧数据被转移到下一轮。

所以为了简单起见,让我们说程序 B 写

啊啊

程序 A 会正确读取,

那么程序 B 的下一个写入是:

b

程序 A 读取

似乎应该有一些简单的方法来刷新内存映射文件的内容,但我似乎无法弄清楚。我的处理方式很可能完全错了。

这是我目前正在做的事情。

程序 A:

using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap",MemoryMappedFileRights.ReadWrite))
{

    Mutex mutex = Mutex.OpenExisting("testmapmutex");
    mutex.WaitOne();
    string outputtext;
    using (MemoryMappedViewStream stream = mmf.CreateViewStream(0,0))
    {

        XmlSerializer deserializer = new XmlSerializer(typeof(MyObject));
        TextReader textReader = new StreamReader(stream);
        outputtext = textReader.ReadToEnd();
        textReader.Close();

    }

    mutex.ReleaseMutex();
    return outputtext; //ends up in a textbox for debugging

}

程序 B

using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap", MemoryMappedFileRights.ReadWrite))
{

    Mutex mutex = Mutex.OpenExisting("testmapmutex");
    mutex.WaitOne();

    using (MemoryMappedViewStream stream = mmf.CreateViewStream(0, 0))
    {


        XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
        TextWriter textWriter = new StreamWriter(stream);
        serializer.Serialize(textWriter, myObjectToExport);
        textWriter.Flush();

    }
    mutex.ReleaseMutex();

}

【问题讨论】:

  • 我想知道stream.SetLength(stream.Position);在这种情况下是否可以在当前位置截断文件。
  • 您的第一次尝试是偶然的,MMF 仍然包含二进制零,XmlSerializer 会忽略它们。第二次尝试没有这样的运气。您传递给 CreateViewStream() 的参数很重要,您必须传递正确的大小。这是一个令人讨厌的先有鸡还是先有蛋的问题,也需要进入 MMF。 XML 不适合 MMF 还有一个特别讨厌的问题。您完全没有在这里获得 MMF 的好处,因为它们在托管代码中很少,所以使用命名管道要好得多。
  • 你说得对。有人指出我昨天应该使用命名管道,但随后删除了他的答案。我今天把整个东西都换了,我认为它对我有用。
  • 我是否应该删除整个问题,因为我已经放弃了这个策略而支持命名管道?
  • 我无法为同一问题找到可接受的解决方案,因此我的解决方法是为每条消息传递一个 GUID,客户端将其跟踪为 LastGuid,然后反序列化当前消息 Guid 是否为同样它忽略该消息。对于应该直截了当的事情,仍然感觉像是一个糟糕的黑客解决方法。

标签: c#


【解决方案1】:

假设 length 相当小,您可以真的清除它

textWriter.BaseStream.Seek(0, System.IO.SeekOrigin.Begin);
textWriter.BaseStream.Write(new byte[length], 0, length);
textWriter.BaseStream.Seek(0, System.IO.SeekOrigin.Begin);

编辑:我想我误解了 OP 的问题。他遇到的问题不是清除MMF 的内容,而是流操作。这应该可以解决问题:

textWriter.BaseStream.Seek(0, System.IO.SeekOrigin.Begin);
textWriter.Write("");
textWriter.Flush();

话虽如此,你可能想要同时做这两个。

【讨论】:

    【解决方案2】:

    我并没有真正使用过 MemoryMappedStreams,但这个问题似乎很有趣,所以我尝试了一下。我用两个按钮(读/写)和一个文本框编写了一个非常基本的 Windows 示例。我没有将“0, 0”传递给 CreateViewStream 调用,而是使用对“CreateOrOpen”的调用创建了具有固定长度的文件,一切正常!以下是我编写的关键代码:

    写文件

    // create the file if it doesn't exist
    if (sharedFile == null) sharedFile = MemoryMappedFile.CreateOrOpen("testmap", 1000, MemoryMappedFileAccess.ReadWrite);
    
    // process safe handling
    Mutex mutex = new Mutex(false, "testmapmutex");
    
    if (mutex.WaitOne()) {
        try {
            using (MemoryMappedViewStream stream = sharedFile.CreateViewStream()) {
                var writer = new StreamWriter(stream);
                writer.WriteLine(txtResult.Text);
                writer.Flush();
            }
        }
        finally { mutex.ReleaseMutex(); }
    }
    

    阅读文件

    // create the file if it doesn't exist
    if (sharedFile == null) sharedFile = MemoryMappedFile.CreateOrOpen("testmap", 1000, MemoryMappedFileAccess.ReadWrite);
    
    // process safe handling
    Mutex mutex = new Mutex(false, "testmapmutex");
    
    if (mutex.WaitOne()) {
        try {
            using (MemoryMappedViewStream stream = sharedFile.CreateViewStream()) {
                var textReader = new StreamReader(stream);
                txtResult.Text = textReader.ReadToEnd();
                textReader.Close();
            }
        }
        finally { mutex.ReleaseMutex(); }
    }
    

    处理文件(完成后)

    if (sharedFile != null) sharedFile.Dispose();
    

    有关完整示例,请参见此处:https://github.com/goopyjava/memory-map-test。希望对您有所帮助!

    编辑/注释 - 如果您查看提供的示例,您可以根据需要多次写入文件,并且在您阅读时,您将准确/仅阅读最后写入的内容。我相信这是问题的最初目标。

    【讨论】:

    • 你根本没有解决OP的问题,你只解决了他的第一次尝试。
    • @HansPassant 如果您试用我发布的测试程序,它实际上会在您每次运行读取或写入代码块时“清除文件”。本质上,通过在字符串末尾写入空字符,这似乎有效 - 不仅是第一次,而且之后的每次也是如此。换句话说,我确实解决了多次写入同一个共享文件的问题的核心。请下载/运行代​​码,我认为您应该会看到相同的结果。
    • “清除文件”是有帮助的,不要在你的代码中忽略它。 MMF 的重点是效率。仅仅为了传输 1 个字节就必须“清除”很多字节效率不高。 “清除”工作只是一个意外,XmlSerializer 恰好忽略了二进制零。 1000 字节的工作也只是一个意外,显然 XML 可能比这更多。事故太多了。
    • @HansPassant 你是对的,固定大小的文件可能是个问题。这是 OP 没有问的,所以我没有具体说明。我们没有看到他们是如何创建原始文件的,所以我不知道他们是如何处理这个标准的。至于“清除”,我的示例的重点是您根本不需要这样做 - 只需以特殊字符终止(例如 '\0')。在我的示例中,如果您在序列化时以空字符终止,则足以允许读取/写入任意次数而无需清除。我假设 xml 序列化例程具有类似的选项。
    • 嘿,感谢您对此进行调查,我最初也有相同的想法以终止一个特殊字符,但我无法弄清楚。昨天有人指出命名管道更适合我的应用程序,我今天将其全部切换。看起来那个答案被删除了。
    猜你喜欢
    • 2016-12-08
    • 1970-01-01
    • 1970-01-01
    • 2020-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-17
    • 1970-01-01
    相关资源
    最近更新 更多