c#_內存映射文件

內存映射文件

內存映射文件包含虛擬內存中文件的內容。 藉助文件和內存空間之間的這種映射,應用(包括多個進程)可以直接對內存執行讀取和寫入操作,從而修改文件。 從 .NET Framework 4 開始,可以使用託管代碼訪問內存映射文件,就像本機 Windows 函數訪問內存映射文件(如管理內存映射文件所述)一樣。
內存映射文件分爲兩種類型:

  • 持久化內存映射文件
    持久化文件是與磁盤上的源文件相關聯的內存映射文件。 當最後一個進程處理完文件時,數據保存到磁盤上的源文件中。 此類內存映射文件適用於處理非常大的源文件。
  • 非持久化內存映射文件
    非持久化文件是不與磁盤上的文件相關聯的內存映射文件。 當最後一個進程處理完文件時,數據會丟失,且文件被垃圾回收器回收。 此類文件適合創建共享內存,以進行進程內通信 (IPC)。

進程、視圖和管理內存

可以跨多個進程共享內存映射文件。 進程可以映射到相同的內存映射文件,只需使用文件創建進程分配的通用名稱即可。

必須創建整個或部分內存映射文件的視圖,才能使用內存映射文件。 還可以爲內存映射文件的同一部分創建多個視圖,從而創建併發內存。 若要讓兩個視圖一直處於併發狀態,必須通過同一個內存映射文件創建它們。

如果文件大於可用於內存映射的應用邏輯內存空間(在 32 位計算機中爲 2GB),可能也有必要使用多個視圖。

視圖分爲以下兩種類型:流訪問視圖和隨機訪問視圖。 使用流訪問視圖,可以順序訪問文件;建議對非持久化文件和 IPC 使用這種類型。 隨機訪問視圖是處理持久化文件的首選類型。

由於內存映射文件是通過操作系統的內存管理程序進行訪問,因此文件會被自動分區到很多頁面,並根據需要進行訪問。 無需自行處理內存管理。

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

持久化內存映射文件

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;

class Program
{
    static void Main(string[] args)
    {
        long offset = 0x10000000; // 256 megabytes
        long length = 0x20000000; // 512 megabytes

        // Create the memory-mapped file.
        using (var mmf = MemoryMappedFile.CreateFromFile(@"c:\ExtremelyLargeImage.data", FileMode.Open,"ImgA"))
        {
            // Create a random access view, from the 256th megabyte (the offset)
            // to the 768th megabyte (the offset plus length).
            using (var accessor = mmf.CreateViewAccessor(offset, length))
            {
                int colorSize = Marshal.SizeOf(typeof(MyColor));
                MyColor color;

                // Make changes to the view.
                for (long i = 0; i < length; i += colorSize)
                {
                    accessor.Read(i, out color);
                    color.Brighten(10);
                    accessor.Write(i, ref color);
                }
            }
        }
    }
}

public struct MyColor
{
    public short Red;
    public short Green;
    public short Blue;
    public short Alpha;

    // Make the view brighter.
    public void Brighten(short value)
    {
        Red = (short)Math.Min(short.MaxValue, (int)Red + value);
        Green = (short)Math.Min(short.MaxValue, (int)Green + value);
        Blue = (short)Math.Min(short.MaxValue, (int)Blue + value);
        Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value);
    }
}

下面的示例爲另一個進程打開相同的內存映射文件。

using System;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;

class Program
{
    static void Main(string[] args)
    {
        // Assumes another process has created the memory-mapped file.
        using (var mmf = MemoryMappedFile.OpenExisting("ImgA"))
        {
            using (var accessor = mmf.CreateViewAccessor(4000000, 2000000))
            {
                int colorSize = Marshal.SizeOf(typeof(MyColor));
                MyColor color;

                // Make changes to the view.
                for (long i = 0; i < 1500000; i += colorSize)
                {
                    accessor.Read(i, out color);
                    color.Brighten(20);
                    accessor.Write(i, ref color);
                }
            }
        }
    }
}

public struct MyColor
{
    public short Red;
    public short Green;
    public short Blue;
    public short Alpha;

    // Make the view brigher.
    public void Brighten(short value)
    {
        Red = (short)Math.Min(short.MaxValue, (int)Red + value);
        Green = (short)Math.Min(short.MaxValue, (int)Green + value);
        Blue = (short)Math.Min(short.MaxValue, (int)Blue + value);
        Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value);
    }
}

非持久化內存映射文件

CreateNew 和 CreateOrOpen 方法創建未映射到磁盤上現有文件的內存映射文件。

下面的示例包含三個獨立進程(控制檯應用),以將布爾值寫入內存映射文件。 各操作按下面的順序發生:

  1. Process A 創建內存映射文件,並向其中寫入值。
  2. Process B 打開內存映射文件,並向其中寫入值。
  3. Process C 打開內存映射文件,並向其中寫入值。
  4. Process A 讀取並顯示內存映射文件中的值。
  5. 在 Process A 處理完內存映射文件後,此文件立即被垃圾回收器回收。

若要運行此示例,請按照以下步驟操作:
編譯應用並打開三個命令提示符窗口。

  1. 在第一個命令提示符窗口中,運行 Process A。
  2. 在第二個命令提示符窗口中,運行 Process B。
  3. 返回到 Process A,再按 Enter。
  4. 在第三個命令提示符窗口中,運行 Process C。
  5. 返回到 Process A,再按 Enter。

Process A

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;

class Program
{
    // Process A:
    static void Main(string[] args)
    {
        using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("testmap", 10000))
        {
            bool mutexCreated;
            Mutex mutex = new Mutex(true, "testmapmutex", out mutexCreated);
            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            {
                BinaryWriter writer = new BinaryWriter(stream);
                writer.Write(1);
            }
            mutex.ReleaseMutex();

            Console.WriteLine("Start Process B and press ENTER to continue.");
            Console.ReadLine();

            Console.WriteLine("Start Process C and press ENTER to continue.");
            Console.ReadLine();

            mutex.WaitOne();
            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            {
                BinaryReader reader = new BinaryReader(stream);
                Console.WriteLine("Process A says: {0}", reader.ReadBoolean());
                Console.WriteLine("Process B says: {0}", reader.ReadBoolean());
                Console.WriteLine("Process C says: {0}", reader.ReadBoolean());
            }
            mutex.ReleaseMutex();
        }
    }
}

Process B

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;

class Program
{
    // Process B:
    static void Main(string[] args)
    {
        try
        {
            using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap"))
            {

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

                using (MemoryMappedViewStream stream = mmf.CreateViewStream(1, 0))
                {
                    BinaryWriter writer = new BinaryWriter(stream);
                    writer.Write(0);
                }
                mutex.ReleaseMutex();
            }
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine("Memory-mapped file does not exist. Run Process A first.");
        }
    }
}

Process C

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;

class Program
{
    // Process C:
    static void Main(string[] args)
    {
        try
        {
            using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap"))
            {

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

                using (MemoryMappedViewStream stream = mmf.CreateViewStream(2, 0))
                {
                    BinaryWriter writer = new BinaryWriter(stream);
                    writer.Write(1);
                }
                mutex.ReleaseMutex();
            }
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine("Memory-mapped file does not exist. Run Process A first, then B.");
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章