C# 进程间通过内存映射文件通信

内存映射文件(Memory-mapped files)是一种很好的进程间通信方式,它暴露了底层的细节,具有很强的扩展性以及性能。

这里展示一个利用内存映射文件制作的 变量同步工具。

该工具当前存在的问题是:

  1. 每次会同步变量的所有字段,不能针对某个字段进行同步。
  2. 没有使用双缓冲,超出64位的字段会出现问题。
  3. 一个类一个文件,没能充分利用文件的空间。

工具代码

public interface ISerializer<T>
    where T :ISerializer<T>
{
    void Serialize(BinaryWriter writer);
    void Deserialize(BinaryReader reader);
}
public class VariableAllocator
{
    public static VariableProxy<T> NewPub<T>() 
        where T : ISerializer<T>,new()
    {
        return NewPub<T>("", 4096);
    }

    public static VariableProxy<T> NewPub<T>(string mapNamePrefix, int capacity) 
        where T: ISerializer<T>,new()
    {
        MemoryMappedFile memoryMappedFile = MemoryMappedFile.CreateNew(mapNamePrefix+typeof(T).Name, capacity);

        var publisher = new VariableProxy<T>(memoryMappedFile);

        return publisher;
    }

    public static VariableProxy<T> NewSub<T>() 
        where T: ISerializer<T>,new()
    {
        return NewSub<T>("");
    }

    public static VariableProxy<T> NewSub<T>(string mapNamePrefix)
        where T: ISerializer<T>,new()
    {
        MemoryMappedFile memoryMappedFile = MemoryMappedFile.OpenExisting(mapNamePrefix + typeof(T).Name);

        var publisher = new VariableProxy<T>(memoryMappedFile);

        return publisher;
    }

}
{
public class VariableProxy<T> : VariableProxy
    where T: ISerializer<T>, new()
{

    public VariableProxy(MemoryMappedFile memoryMappedFile) : base(memoryMappedFile)
    {
    }

    public void Publish(T val)
    {
        memoryMappedViewStream.Position = 0;
        val.Serialize(writer);
    }

    public T Subscribe()
    {
        memoryMappedViewStream.Position = 0;
        var val = new T();
        val.Deserialize(reader);
        return val;
    }
}

public class VariableProxy:IDisposable
{
    protected MemoryMappedFile memoryMappedFile;

    protected MemoryMappedViewStream memoryMappedViewStream;

    protected BinaryWriter writer;
    protected BinaryReader reader;


    public VariableProxy(MemoryMappedFile memoryMappedFile)
    {
        this.memoryMappedFile = memoryMappedFile;
        memoryMappedViewStream = memoryMappedFile.CreateViewStream();
        writer = new BinaryWriter(memoryMappedViewStream);
        reader = new BinaryReader(memoryMappedViewStream);
    }

    public void Dispose()
    {
        memoryMappedFile.Dispose();
        memoryMappedViewStream.Dispose();
        writer.Dispose();
        reader.Dispose();
    }

}

变量代码

    public class Player:ISerializer<Player>
    {
        public int Id { get; set; }
        public int Age { get; set; }
        public int Hp { get; set; }

        public void Deserialize(BinaryReader stream)
        {
            Id = stream.ReadInt32();
            Age = stream.ReadInt32();
            Hp = stream.ReadInt32();
        }

        public void Serialize(BinaryWriter stream)
        {
            stream.Write(Id);
            stream.Write(Age);
            stream.Write(Hp);
        }
    }

发送方代码

    class Program
    {
        // Process A:
        static void Main(string[] args)
        {
            Player player = new Player { Id = 1001, Age = 23, Hp = 40000 };

            using (var pub = VariableAllocator.NewPub<Player>())
            {
                while (true)
                {
                    pub.Publish(player);

                    player.Hp -= 1;
                    Thread.Sleep(1000);
                }
            }
        }
    }

接收方代码

    static void Main(string[] args)
    {
        try
        {
            using(var sub = VariableAllocator.NewSub<Player>())
            {

                while (true)
                {
                    Player player = sub.Subscribe();

                    Console.WriteLine($"ID={player.Id} Age={player.Age} HP={player.Hp}");
                }

            }
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine("Memory-mapped file does not exist. Run Process A first.");
        }
    }
posted @ 2023-07-10 22:10  dewxin  阅读(377)  评论(0编辑  收藏  举报