(.Net) 使用内存映射文件的简单方法。就像一个变量

(.Net) Easy way to use Memory Mapped Files. Just like a variable

我正在开发 ASP.NET 网络应用程序。它需要大量的内存。因此,我正在考虑内存映射文件。

但是,实现内存映射文件非常累人。它需要使用那些内存地址、SizeOf 运算符等。(我 运行 没时间了)。

当前系统使用大字典来存储大量数据。所有 类 都是 [Serializable]。 有没有办法像这样结束对内存映射文件的访问

(C# Psedo-code)    
var WrittingObject = new blablabla;
SetMMFVariable(name: "var1", value : WrittingObject);
var ReadingObject = GetMMFVariable(name: "var1");

.NET 中是否有内存映射文件的包装代码?我不必担心那些内存地址和其他东西。

这将是一个很好的数据库候选者!您遇到的问题是一次 serializing/deserializing 大量信息。如果您需要读取或更新一小段信息,则需要扫描整个文件以更新单个记录。这太贵了!

解决方法是使用一些结构化数据模型。您可以分发模型 read/writes 以使用 OS 上的许多小文件(可能会损坏、磁盘问题、权限问题、多重访问和文件锁定)来解决上述序列化问题,或者您可以转到一个实际的数据库,它将允许您查询 & update/insert 您需要的信息。

对于结构化模型(c# 擅长此),请查看 Sql Server Express https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/sql-server-express-localdb?view=sql-server-2017

对于非结构化信息 (json) 查看非关系型信息,例如 couchbase https://docs.couchbase.com/dotnet-sdk/2.7/start-using-sdk.html

我做了一个解决方案。 这是一个有点乱的代码。它与项目的其他部分有关。但是你可以让它很容易携带。

(Visual Basic .NET 代码)

Imports System.IO
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.Web.UI

Public Module UserStates

Public Property CurrSessionVariableIndex As Integer
    Get

        Dim I As Integer
        If LDB.CustomData.TryGetValue("CurrSessionIndex", I) Then
            Return I
        Else
            LDB.CustomData("CurrSessionIndex") = 1
            Return I
        End If

    End Get
    Set(value As Integer)
        LDB.CustomData("CurrSessionIndex") = value
    End Set
End Property

Public Function NewSessionVariableIndex() As Integer

    CurrSessionVariableIndex += 1
    Return CurrSessionVariableIndex

End Function


Public PreferMemoryMapFiles As Boolean = True




Public ViewStatusBags As New Dictionary(Of Integer, Dictionary(Of String, Object))(300)
Public ViewStatusTimes As New Dictionary(Of Integer, DateTime)(300)

Public Sub SetViewVarRAM(ViewStateID As Integer, key As String, Obj As Object)
    Dim Bag As Dictionary(Of String, Object) = Nothing
    If ViewStatusBags.TryGetValue(ViewStateID, Bag) Then
        Try
            Bag(key) = Obj
        Catch ex As Exception
            Log($"Added new ViewBag.Var {ViewStateID}.{key}")
            Bag.Add(key, Obj)
        End Try
        ViewStatusTimes(ViewStateID) = Now
    Else
        Log($"Added new ViewBag {ViewStateID}")
        Bag = New Dictionary(Of String, Object)
        ViewStatusBags.Add(ViewStateID, Bag)
        ViewStatusTimes.Add(ViewStateID, Now)
        Bag.Add(key, Obj)

    End If

End Sub

Public Function GetViewVarRAM(ViewStateID As Integer, key As String) As Object
    Dim Bag As Dictionary(Of String, Object) = Nothing
    If ViewStatusBags.TryGetValue(ViewStateID, Bag) Then
        ViewStatusTimes(ViewStateID) = Now
        Try
            Return Bag(key)
        Catch ex As Exception
            Log($"Added new ViewBag.Var {ViewStateID}.{key}")
            Bag.Add(key, Nothing)
            Return Bag(key)
        End Try
    Else
        Log($"Added new ViewBag {ViewStateID}")
        Bag = New Dictionary(Of String, Object)
        ViewStatusBags.Add(ViewStateID, Bag)
        ViewStatusTimes.Add(ViewStateID, Now)

        Bag.Add(key, Nothing)
        Return Bag(key)
    End If
End Function



Public Function GetViewMMF(ViewStateID As Integer) As Dictionary(Of String, Object)
    Dim FS As FileStream = Nothing
    Dim BF As New BinaryFormatter

    GetFileStream(ViewStateID, FS)



    Dim Bag As Dictionary(Of String, Object)

    Try
        Bag = BF.Deserialize(FS)
    Catch ex As Exception
        If Not ex.Message.StartsWith("Attempting to deserialize an empty stream") Then
            LogError(ex, $"@MMF.Get : cannot Deserialize FS {ViewStateID}")
        End If
        FS.Flush()
        Bag = New Dictionary(Of String, Object)
        SetViewMMF(ViewStateID, Bag)
    End Try

    Try
        FS.Flush()
    Catch ex As Exception

    End Try

    Return Bag

End Function

Public Sub SetViewMMF(ViewStateID As Integer, Bag As Dictionary(Of String, Object))

    Dim FS As FileStream = Nothing
    Dim BF As New BinaryFormatter


    GetFileStream(ViewStateID, FS)


    Try
        BF.Serialize(FS, Bag)
    Catch ex As Exception
        LogError(ex, $"@MMF.Set : cannot Serialize FS {ViewStateID}")
        FS.Flush()
    End Try

    Try
        FS.Flush()
    Catch ex As Exception

    End Try
End Sub



Public MMFStreams As New Dictionary(Of Integer, FileStream)(100)
Public MMFStreamsTimes As New Dictionary(Of Integer, Date)(100)
Public FilePathMMFs As String

Private Sub GetFileStream(ByRef ViewStateID As Integer, ByRef FS As FileStream)
    If MMFStreams.TryGetValue(ViewStateID, FS) Then
        Try
            FS.Seek(0, SeekOrigin.Begin)
        Catch ex As Exception
            LogError(ex, $"@MMF.Set : cannot restart FS {ViewStateID}")
            FS = New FileStream(FilePathMMFs & ViewStateID.ToString & "-" & Rand.Next(100, 1000).ToString, FileMode.OpenOrCreate)
            FS.Seek(0, SeekOrigin.Begin)
            MMFStreams(ViewStateID) = FS
        End Try

        MMFStreamsTimes(ViewStateID) = Now
    Else

        Try
            FS = New FileStream(FilePathMMFs & ViewStateID.ToString, FileMode.OpenOrCreate)
            FS.Seek(0, SeekOrigin.Begin)
            MMFStreams.Add(ViewStateID, FS)
        Catch ex As Exception
            LogError(ex, $"@MMF.Set : cannot OpenOrCreate new FS {ViewStateID}")
            FS = New FileStream(FilePathMMFs & ViewStateID.ToString & "-" & Rand.Next(100, 1000).ToString, FileMode.OpenOrCreate)
            FS.Seek(0, SeekOrigin.Begin)
            MMFStreams.Add(ViewStateID, FS)

        End Try

        MMFStreamsTimes(ViewStateID) = Now
    End If
End Sub








Public Sub SetViewVarMMF(ViewStateID As Integer, key As String, Obj As Object)
    Dim FS As FileStream = Nothing
    Dim BF As New BinaryFormatter

    GetFileStream(ViewStateID, FS)
    Dim Bag As Dictionary(Of String, Object)

    Try
        Bag = BF.Deserialize(FS)
    Catch ex As Exception
        'LogError(ex, $"@MMF.SetViewVarMMF : cannot Deserialize FS {ViewStateID}. saved new bag")
        Bag = New Dictionary(Of String, Object)
    End Try

    FS.Seek(0, SeekOrigin.Begin)

    If Bag.ContainsKey(key) Then
        Bag(key) = Obj
    Else
        Bag.Add(key, Obj)
    End If


    Try
        BF.Serialize(FS, Bag)
    Catch ex As Exception
        LogError(ex, $"@MMF.SetViewVarMMF : cannot Serialize FS {ViewStateID}")
        FS.Flush()
    End Try

    Try
        FS.Flush()
    Catch ex As Exception

    End Try
End Sub



Public Function GetViewVarMMF(ViewStateID As Integer, key As String) As Object

    Dim Bag = GetViewMMF(ViewStateID)

    Dim Obj As Object = Nothing
    If Bag.TryGetValue(key, Obj) Then
        Return Obj
    Else

        Return Nothing
    End If

    Bag = Nothing

End Function






Public AllUserContainer As New Dictionary(Of Integer, UserContainer)(50)



Public Sub SetUserContainer(Usr As Integer, Con As UserContainer)
    Con.UserID = Usr
    If AllUserContainer.ContainsKey(Usr) Then
        AllUserContainer(Usr) = Con
        Con.Time = Now
    Else
        Log($"Added new user {Usr}")
        AllUserContainer.Add(Usr, Con)
        Con.Time = Now
    End If

End Sub

Public Function GetUserContainer(Usr As Integer) As UserContainer
    Dim Con As UserContainer = Nothing

    If AllUserContainer.TryGetValue(Usr, Con) Then
        Con.Time = Now
        Return Con
    Else
        Log($"Added new user {Usr}")
        Con = New UserContainer(Usr)
        AllUserContainer.Add(Usr, Con)
        Con.Time = Now
        Return Con
    End If
End Function





'___________________________________________________'


Public Sub SetViewVar(ViewStateID As Integer, key As String, Obj As Object)
    If PreferMemoryMapFiles Then
        SetViewVarMMF(ViewStateID, key, Obj)
    Else
        SetViewVarRAM(ViewStateID, key, Obj)
    End If
End Sub




Public Function GetViewVar(ViewStateID As Integer, key As String) As Object
    If PreferMemoryMapFiles Then
        Return GetViewVarMMF(ViewStateID, key)
    Else
        Return GetViewVarRAM(ViewStateID, key)
    End If
End Function


Public Sub RemoveMMF(ViewStateID As Integer)

    Dim FS As FileStream = Nothing

    If MMFStreams.TryGetValue(ViewStateID, FS) Then
        Try
            FS.Close()
        Catch ex As Exception

        End Try

    End If

End Sub

Public Sub RemoveUserContainer(Usr As Integer)
    If AllUserContainer.ContainsKey(Usr) Then
        AllUserContainer(Usr).Dispose()
        AllUserContainer.Remove(Usr)
        Log($"Removed user {Usr}")
    End If

End Sub



Public Sub DeleteUnusedMMFs()

    Dim Files = Directory.GetFiles(FilePathMMFs)

    Dim nowTime As Date = Now

    Dim nOfFiles = 0I

    For Each FilePath In Files
        Dim FileID = Path.GetFileName(FilePath)

        Dim Time As Date = Date.MinValue
        If UserStates.MMFStreamsTimes.TryGetValue(FileID, Time) Then
            If Time.AddMinutes(5) < nowTime Then
                Try
                    UserStates.MMFStreams(FileID).Close()
                Catch ex As Exception
                End Try
                Try
                    File.Delete(FilePath)
                    nOfFiles += 1
                Catch ex As Exception

                End Try
            End If

        Else
            Try
                File.Delete(FilePath)
                nOfFiles += 1
            Catch ex As Exception

            End Try
        End If

    Next


    Log($"@ViewStates.DeleteUnusedMMFs : {nOfFiles} deleted")



End Sub


'___________________________________________________'


Public Sub ClearUnusedViewStates()

    Dim BeforeRAM = (My.Application.Info.WorkingSet / 1024) / 1024

    Dim CurrTime = Now

    Dim ToRemove As New List(Of Integer)
    Dim ViewEntries As Integer
    Dim UsrEntries As Integer

    If ViewStatusBags.Count = 0 Then
        GoTo RemoveusrsLbl
    End If

    For i = ViewStatusBags.Count - 1 To 0 Step -1
        Dim Time = ViewStatusTimes.ElementAt(i).Value

        If Time.AddMinutes(5) < CurrTime Then
            ToRemove.Add(ViewStatusBags.ElementAt(i).Key)
        End If
    Next

    ViewEntries = ToRemove.Count
    For Each i In ToRemove
        ViewStatusBags.Remove(i)
    Next


 RemoveusrsLbl:
    If AllUserContainer.Count = 0 Then
        GoTo Final
    End If

    For i = AllUserContainer.Count - 1 To 0 Step -1

        If AllUserContainer(i).Time.AddMinutes(15) < CurrTime Then
            AllUserContainer.Remove(AllUserContainer(i).UserID)
            UsrEntries += 1
        End If
    Next



Final:

    GC.Collect()


    Dim AfterRAM = (My.Application.Info.WorkingSet / 1024) / 1024


    Log($"@ViewStates : {ViewEntries} Unused ViewStates Cleared. {UsrEntries} inactive users cleared. {AfterRAM - BeforeRAM} MB Cleared. ")

End Sub

End Module

(C# 代码)(已翻译)

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualBasic;
using System.Runtime.Serialization.Formatters.Binary;
using System.Web.UI;

public static class UserStates
{
    public static int CurrSessionVariableIndex
    {
        get
        {
            int I;
            if (LDB.CustomData.TryGetValue("CurrSessionIndex", I))
                return I;
            else
            {
                LDB.CustomData("CurrSessionIndex") = 1;
                return I;
            }
        }
        set
        {
            LDB.CustomData("CurrSessionIndex") = value;
        }
    }

    public static int NewSessionVariableIndex()
    {
        CurrSessionVariableIndex += 1;
        return CurrSessionVariableIndex;
    }


    public static bool PreferMemoryMapFiles = true;




    public static Dictionary<int, Dictionary<string, object>> ViewStatusBags = new Dictionary<int, Dictionary<string, object>>(300);
    public static Dictionary<int, DateTime> ViewStatusTimes = new Dictionary<int, DateTime>(300);

    public static void SetViewVarRAM(int ViewStateID, string key, object Obj)
    {
        Dictionary<string, object> Bag = null;
        if (ViewStatusBags.TryGetValue(ViewStateID, out Bag))
        {
            try
            {
                Bag[key] = Obj;
            }
            catch (Exception ex)
            {
                Log($"Added new ViewBag.Var {ViewStateID}.{key}");
                Bag.Add(key, Obj);
            }
            ViewStatusTimes[ViewStateID] = DateTime.Now;
        }
        else
        {
            Log($"Added new ViewBag {ViewStateID}");
            Bag = new Dictionary<string, object>();
            ViewStatusBags.Add(ViewStateID, Bag);
            ViewStatusTimes.Add(ViewStateID, DateTime.Now);
            Bag.Add(key, Obj);
        }
    }

    public static object GetViewVarRAM(int ViewStateID, string key)
    {
        Dictionary<string, object> Bag = null;
        if (ViewStatusBags.TryGetValue(ViewStateID, out Bag))
        {
            ViewStatusTimes[ViewStateID] = DateTime.Now;
            try
            {
                return Bag[key];
            }
            catch (Exception ex)
            {
                Log($"Added new ViewBag.Var {ViewStateID}.{key}");
                Bag.Add(key, null);
                return Bag[key];
            }
        }
        else
        {
            Log($"Added new ViewBag {ViewStateID}");
            Bag = new Dictionary<string, object>();
            ViewStatusBags.Add(ViewStateID, Bag);
            ViewStatusTimes.Add(ViewStateID, DateTime.Now);

            Bag.Add(key, null);
            return Bag[key];
        }
    }



    public static Dictionary<string, object> GetViewMMF(int ViewStateID)
    {
        FileStream FS = null;
        BinaryFormatter BF = new BinaryFormatter();

        GetFileStream(ref ViewStateID, ref FS);



        Dictionary<string, object> Bag;

        try
        {
            Bag = BF.Deserialize(FS);
        }
        catch (Exception ex)
        {
            if (!ex.Message.StartsWith("Attempting to deserialize an empty stream"))
                LogError(ex, $"@MMF.Get : cannot Deserialize FS {ViewStateID}");
            FS.Flush();
            Bag = new Dictionary<string, object>();
            SetViewMMF(ViewStateID, Bag);
        }

        try
        {
            FS.Flush();
        }
        catch (Exception ex)
        {
        }

        return Bag;
    }

    public static void SetViewMMF(int ViewStateID, Dictionary<string, object> Bag)
    {
        FileStream FS = null;
        BinaryFormatter BF = new BinaryFormatter();


        GetFileStream(ref ViewStateID, ref FS);


        try
        {
            BF.Serialize(FS, Bag);
        }
        catch (Exception ex)
        {
            LogError(ex, $"@MMF.Set : cannot Serialize FS {ViewStateID}");
            FS.Flush();
        }

        try
        {
            FS.Flush();
        }
        catch (Exception ex)
        {
        }
    }



    public static Dictionary<int, FileStream> MMFStreams = new Dictionary<int, FileStream>(100);
    public static Dictionary<int, DateTime> MMFStreamsTimes = new Dictionary<int, DateTime>(100);
    public static string FilePathMMFs;

    private static void GetFileStream(ref int ViewStateID, ref FileStream FS)
    {
        if (MMFStreams.TryGetValue(ViewStateID, out FS))
        {
            try
            {
                FS.Seek(0, SeekOrigin.Begin);
            }
            catch (Exception ex)
            {
                LogError(ex, $"@MMF.Set : cannot restart FS {ViewStateID}");
                FS = new FileStream(FilePathMMFs + ViewStateID.ToString() + "-" + Rand.Next(100, 1000).ToString, FileMode.OpenOrCreate);
                FS.Seek(0, SeekOrigin.Begin);
                MMFStreams[ViewStateID] = FS;
            }

            MMFStreamsTimes[ViewStateID] = DateTime.Now;
        }
        else
        {
            try
            {
                FS = new FileStream(FilePathMMFs + ViewStateID.ToString(), FileMode.OpenOrCreate);
                FS.Seek(0, SeekOrigin.Begin);
                MMFStreams.Add(ViewStateID, FS);
            }
            catch (Exception ex)
            {
                LogError(ex, $"@MMF.Set : cannot OpenOrCreate new FS {ViewStateID}");
                FS = new FileStream(FilePathMMFs + ViewStateID.ToString() + "-" + Rand.Next(100, 1000).ToString, FileMode.OpenOrCreate);
                FS.Seek(0, SeekOrigin.Begin);
                MMFStreams.Add(ViewStateID, FS);
            }

            MMFStreamsTimes[ViewStateID] = DateTime.Now;
        }
    }








    public static void SetViewVarMMF(int ViewStateID, string key, object Obj)
    {
        FileStream FS = null;
        BinaryFormatter BF = new BinaryFormatter();

        GetFileStream(ref ViewStateID, ref FS);
        Dictionary<string, object> Bag;

        try
        {
            Bag = BF.Deserialize(FS);
        }
        catch (Exception ex)
        {
            // LogError(ex, $"@MMF.SetViewVarMMF : cannot Deserialize FS {ViewStateID}. saved new bag")
            Bag = new Dictionary<string, object>();
        }

        FS.Seek(0, SeekOrigin.Begin);

        if (Bag.ContainsKey(key))
            Bag[key] = Obj;
        else
            Bag.Add(key, Obj);


        try
        {
            BF.Serialize(FS, Bag);
        }
        catch (Exception ex)
        {
            LogError(ex, $"@MMF.SetViewVarMMF : cannot Serialize FS {ViewStateID}");
            FS.Flush();
        }

        try
        {
            FS.Flush();
        }
        catch (Exception ex)
        {
        }
    }



    public static object GetViewVarMMF(int ViewStateID, string key)
    {
        var Bag = GetViewMMF(ViewStateID);

        object Obj = null;
        if (Bag.TryGetValue(key, out Obj))
            return Obj;
        else
            return null;

        Bag = null;
    }






    public static Dictionary<int, UserContainer> AllUserContainer = new Dictionary<int, UserContainer>(50);



    public static void SetUserContainer(int Usr, UserContainer Con)
    {
        Con.UserID = Usr;
        if (AllUserContainer.ContainsKey(Usr))
        {
            AllUserContainer[Usr] = Con;
            Con.Time = DateTime.Now;
        }
        else
        {
            Log($"Added new user {Usr}");
            AllUserContainer.Add(Usr, Con);
            Con.Time = DateTime.Now;
        }
    }

    public static UserContainer GetUserContainer(int Usr)
    {
        UserContainer Con = null/* TODO Change to default(_) if this is not a reference type */;

        if (AllUserContainer.TryGetValue(Usr, out Con))
        {
            Con.Time = DateTime.Now;
            return Con;
        }
        else
        {
            Log($"Added new user {Usr}");
            Con = new UserContainer(Usr);
            AllUserContainer.Add(Usr, Con);
            Con.Time = DateTime.Now;
            return Con;
        }
    }





    // ___________________________________________________'


    public static void SetViewVar(int ViewStateID, string key, object Obj)
    {
        if (PreferMemoryMapFiles)
            SetViewVarMMF(ViewStateID, key, Obj);
        else
            SetViewVarRAM(ViewStateID, key, Obj);
    }




    public static object GetViewVar(int ViewStateID, string key)
    {
        if (PreferMemoryMapFiles)
            return GetViewVarMMF(ViewStateID, key);
        else
            return GetViewVarRAM(ViewStateID, key);
    }


    public static void RemoveMMF(int ViewStateID)
    {
        FileStream FS = null;

        if (MMFStreams.TryGetValue(ViewStateID, out FS))
        {
            try
            {
                FS.Close();
            }
            catch (Exception ex)
            {
            }
        }
    }

    public static void RemoveUserContainer(int Usr)
    {
        if (AllUserContainer.ContainsKey(Usr))
        {
            AllUserContainer[Usr].Dispose();
            AllUserContainer.Remove(Usr);
            Log($"Removed user {Usr}");
        }
    }



    public static void DeleteUnusedMMFs()
    {
        var Files = Directory.GetFiles(FilePathMMFs);

        DateTime nowTime = DateTime.Now;

        var nOfFiles = 0;

        foreach (var FilePath in Files)
        {
            var FileID = Path.GetFileName(FilePath);

            DateTime Time = DateTime.MinValue;
            if (UserStates.MMFStreamsTimes.TryGetValue(FileID, out Time))
            {
                if (Time.AddMinutes(5) < nowTime)
                {
                    try
                    {
                        UserStates.MMFStreams[FileID].Close();
                    }
                    catch (Exception ex)
                    {
                    }
                    try
                    {
                        File.Delete(FilePath);
                        nOfFiles += 1;
                    }
                    catch (Exception ex)
                    {
                    }
                }
            }
            else
                try
                {
                    File.Delete(FilePath);
                    nOfFiles += 1;
                }
                catch (Exception ex)
                {
                }
        }


        Log($"@ViewStates.DeleteUnusedMMFs : {nOfFiles} deleted");
    }


    // ___________________________________________________'


    public static void ClearUnusedViewStates()
    {
        var BeforeRAM = (My.Application.Info.WorkingSet / (double)1024) / (double)1024;

        var CurrTime = DateTime.Now;

        List<int> ToRemove = new List<int>();
        int ViewEntries;
        int UsrEntries;

        if (ViewStatusBags.Count == 0)
            goto RemoveusrsLbl;

        for (var i = ViewStatusBags.Count - 1; i >= 0; i += -1)
        {
            var Time = ViewStatusTimes.ElementAt(i).Value;

            if (Time.AddMinutes(5) < CurrTime)
                ToRemove.Add(ViewStatusBags.ElementAt(i).Key);
        }

        ViewEntries = ToRemove.Count;
        foreach (var i in ToRemove)
            ViewStatusBags.Remove(i);


        RemoveusrsLbl:
        ;
        if (AllUserContainer.Count == 0)
            goto Final;

        for (var i = AllUserContainer.Count - 1; i >= 0; i += -1)
        {
            if (AllUserContainer[i].Time.AddMinutes(15) < CurrTime)
            {
                AllUserContainer.Remove(AllUserContainer[i].UserID);
                UsrEntries += 1;
            }
        }



    Final:
        ;
        GC.Collect();


        var AfterRAM = (My.Application.Info.WorkingSet / (double)1024) / (double)1024;


        Log($"@ViewStates : {ViewEntries} Unused ViewStates Cleared. {UsrEntries} inactive users cleared. {AfterRAM - BeforeRAM} MB Cleared. ");
    }
}