Monday, July 7, 2014

C# - Memory Mapped File

Problem

Did you ever encounter a requirement which require your application to get some data from another application? For instance, you have created an application A, the value from one of the variable in application A is required to be shared with other application B, C and D, all these application are going to run in one same server. What would you do?

Solution

Today's topic cover about Memory Mapped File. The fastest way to access data no doubt is direct access from the memory. In .NET 4.0, it introduced Memory Mapped File, a file that contain the contents of virtual memory. This file can be read and write by any program as long as you know the file location. The file content is mapped directly to the virtual memory. Therefore, this virtual memory can become a shared memory which can be accessible directly by any application without any constraint.

Memory Mapped File support persisted and non-persisted file mode, persisted mode will keep the data whenever the last process working with the file has ended, the non-persisted mode will not the keep the data, it will be cleared by garbage collector after the last process has finished working on it. For more info, refer HERE and the concept from HERE.

Memory Mapped File can be used to create shared memory, I can create a generic component which allow me to store any object into it and share it with all the application. But, in this post, I just keep it simple and I am writing a simple string shared memory. The string value can be read and changed. The string value changes will be reflected in all the application immediately. Also, the value will be persisted, after the process is being killed or server get rebooted, the string value will still be available.

There is one challenge here. The file can only be accessible by one process one thread at one time because the file is locked automatically whenever a thread is accessing it. Normally, we can use the lock syntax to block other threads entering critical section of code until it has been released, but in order to lock processes, we have to use mutual exclusion (mutex).

Memory Mapped File is available under the System.IO namespace as long as your application target framework is .NET 4.0 or later. Any external assembly reference is not required.

using System.IO;

using System.IO.MemoryMappedFiles;

This is how the shared memory code look like:

public class SharedMemory : IDisposable
{
    //Keep the mutex as static to prevent early garbage collection
    private static Mutex _mutex;
    private static object _numLock;

    static SharedMemory()
    {
        _numLock = new object();
        if (!Mutex.TryOpenExisting("sharedMutex", out _mutex))
        {
            _mutex = new Mutex(true, "sharedMutex");
        }
    }

    public void Set(string value)
    {

        //lock thread
        lock (_numLock)
        {
            //lock process with mutex
            if (_mutex.WaitOne())
            {
                //access memory mapped file (need persistence)
                using (var memMapFile = MemoryMappedFile.CreateFromFile(
                            @"D:\temp\SharedMemoryMap", //file location
                            FileMode.OpenOrCreate,  //create new file if not exist, open if exist
                            "shared", //map name
                            1024)) //size
                {
                    //update the number to memory view
                    using (var stream = memMapFile.CreateViewStream())
                    using (var writer = new BinaryWriter(stream))
                    {
                        writer.Write(value);
                    }
                }

                //release the mutex for other process to access the memory mapped file
                _mutex.ReleaseMutex();
            }
        }

    }

    public string Get()
    {
        string value = null;

        //lock thread
        lock (_numLock)
        {
            //lock process with mutex
            if (_mutex.WaitOne())
            {
                //access memory mapped file (need persistence)
                using (var memMapFile = MemoryMappedFile.CreateFromFile(
                            @"D:\temp\SharedMemoryMap", //file location
                            FileMode.OpenOrCreate,  //create new file if not exist, open if exist
                            "shared", //map name
                            1024)) //size
                {
                    //get last number from memory view
                    using (var stream = memMapFile.CreateViewStream())
                    using (var reader = new BinaryReader(stream))
                    {
                        value = reader.ReadString();
                    }
                }

                //release the mutex for other process to access the memory mapped file
                _mutex.ReleaseMutex();
            }
        }
        return value;
    }

    public void Dispose()
    {
        if (_mutex != null)
            _mutex.Dispose();
    }

}

Now, I have created and started a few console application which all are having the same shared memory assembly reference. Then, call the Get() function to retrieve the memory value, then the Set() function to change the value.

class Program
{
    static void Main(string[] args)
    {
        bool quit = false;

        using (SharedMemory mem = new SharedMemory())
        {
            do
            {
                Console.WriteLine("Current value: " + mem.Get());
                Console.Write("New value: ");

                string input = Console.ReadLine();
                if (input == "quit")
                    quit = true;
                else
                    mem.Set(input);
            }
            while (!quit);
        }
    }

}

Close all the console windows, then reopen one back. You will still be able to get the last persisted string value which you had entered in the last closed console.

If you go to the memory mapped file location which you had specified in the code, you will see a file is created in the folder and the content is in binary form. That's the value in the virtual memory.

If you are interested with my source code, feel free to download from HERE.

1 comment:

  1. whose the dumbass sob who posted this shit lol

    ReplyDelete

Send Transactional SMS with API

This post cover how to send transactional SMS using the Alibaba Cloud Short Message Service API. Transactional SMS usually come with One Tim...