Lavorare con file mappati in memoria in .Net

L'accesso ai file è un'operazione che richiede molte risorse. L'accesso a un file dal disco per un'applicazione è un'operazione che richiede tempo e l'accesso ai dati dalla memoria primaria è sempre più veloce. Quindi, cosa succede se i file su disco da cui la tua applicazione deve leggere o scrivere risiedessero in memoria? Questo è esattamente il punto in cui si inserisce il concetto di file mappati in memoria. In questo articolo esploreremo come lavorare con i file mappati in memoria in .Net.

Presentazione dei file mappati in memoria

Un file mappato alla memoria è un oggetto kernel utilizzato per mappare un file nel disco a una regione nella memoria primaria. I file mappati in memoria possono avere maggiori prestazioni rispetto all'accesso diretto al disco quando si lavora con grandi quantità di dati o immagini di grandi dimensioni. I file mappati in memoria hanno fatto parte dell'API Win32, ma fino a poco tempo fa era possibile utilizzare C ++ o PInvoke per scrivere codice che sfrutta i file mappati in memoria nell'applicazione. Tuttavia, con .Net Framework 4 ora puoi lavorare con file mappati in memoria direttamente dalle tue applicazioni .Net: il runtime ora ti fornisce un wrapper gestito con tutte le classi wrapper necessarie per chiamare l'API Win32. MSDN afferma: "Un file mappato alla memoria contiene il contenuto di un file nella memoria virtuale. Questo mapping tra un file e lo spazio di memoria consente a un'applicazione, inclusi più processi,per modificare il file leggendo e scrivendo direttamente in memoria. "

Perché hai bisogno di file mappati in memoria?

I file mappati in memoria sono una buona scelta quando è necessario lavorare con una grande quantità di dati e si desidera evitare i costi associati al marshalling e all'annullamento del marshalling durante la condivisione dei dati oltre i limiti del processo. I file mappati in memoria sono ottimi per elaborare un file di grandi dimensioni: leggere un file di grandi dimensioni è un'operazione che richiede molte risorse. Con i file mappati in memoria, è possibile mappare una parte specifica del file in memoria ed eseguire operazioni di I / O con quel blocco per accelerare l'accesso.

Un file mappato alla memoria consente di riservare un intervallo di indirizzi di memoria e utilizzare un file su disco come memoria fisica per l'indirizzo riservato. In altre parole, ti consente di riservare uno spazio nella memoria e quindi impegnare l'archiviazione fisica in quella regione. Ciò consente di accedere ai dati sul disco senza la necessità di eseguire operazioni di I / O sui file. I file mappati in memoria consentono inoltre di condividere i dati tra più processi. Il sistema operativo si occupa della gestione della memoria per i file mappati in memoria: non è necessario preoccuparsi di come il file viene partizionato in pagine e gestito. È inoltre possibile applicare la protezione nel file mappato alla memoria utilizzando l'enumerazione MemoryMappedFileAccess come parametro durante la creazione del file mappato alla memoria. 

File mappati alla memoria persistente e non persistente

Esistono essenzialmente due tipi di file mappati in memoria. Questi sono:

Persistente : i file mappati alla memoria persistente sono quelli associati a un file di origine nel disco del sistema. Quando si lavora con questi tipi di file mappati in memoria, i dati vengono conservati nel disco dopo che l'ultimo processo che lavora sul file termina la sua attività.

Non persistente : i file mappati alla memoria non persistente sono quelli che non sono associati a un file su disco. Quando si lavora con questo tipo di file mappati in memoria, i dati non vengono mantenuti dopo che l'ultimo processo che ha lavorato sul file ha terminato il suo lavoro. I file mappati alla memoria non persistente sono ottimi per condividere la memoria per le comunicazioni tra processi.

Creazione di file mappati in memoria persistente

Per creare un file mappato alla memoria persistente, è necessario utilizzare il metodo CreateFromFile della classe MemoryMappedFile. La classe MemorymappedFile è presente nello spazio dei nomi System.IO.MemoryMappedFiles.

Il frammento di codice seguente utilizza il metodo CreateFromFile per creare un file mappato in memoria. Successivamente crea una vista mappata in memoria a una parte del file.

static long offset = 0x10000000; // 256 megabytes

static long length = 0x20000000; // 512 megabytes

        static void Main()

        {

            using (var memoryMappedFile = MemoryMappedFile.CreateFromFile("F:\\ImageData.png", FileMode.Open, "PartitionA"))

            {

                using (var accessor = memoryMappedFile.CreateViewAccessor(offset, length))

                {

                    //Other code

                }

            }

        } 

Lo snippet di codice fornito successivamente mostra come leggere i dati da un file mappato in memoria.

using (MemoryMappedFile memoryMappedFile = MemoryMappedFile.CreateFromFile("F:\\LargeData.dat"))

            {

                using (MemoryMappedViewStream memoryMappedViewStream = memoryMappedFile.CreateViewStream(0, 1204, MemoryMappedFileAccess.Read))

                {

                    var contentArray = new byte[1024];

                    memoryMappedViewStream.Read(contentArray, 0, contentArray.Length);

                    string content = Encoding.UTF8.GetString(contentArray);

                }

            }

Creazione di file mappati alla memoria non persistente

Per creare file mappati alla memoria non persistente, cioè file che non sono mappati su un file esistente sul disco, è necessario utilizzare i metodi CreateNew e CreateOrOpen.

Il frammento di codice seguente illustra come creare un file mappato alla memoria non persistente.

using(MemoryMappedFile memoryMappedFile = MemoryMappedFile.CreateNew("idg.txt", 5))

            {

                using(MemoryMappedViewAccessor memoryMappedViewAccessor = memoryMappedFile.CreateViewAccessor())

                {

                var data = new[] { (byte)'I', (byte)'D', (byte)'G'};

                for (int i = 0; i < data.Length; i++)

                    memoryMappedViewAccessor.Write(i, data[i]);

                memoryMappedViewAccessor.Dispose();

                memoryMappedFile.Dispose();

                }

            }

Puoi saperne di più sui file mappati in memoria da questo articolo di MSDN.