我参与了您之前提出的另一个问题,并且我真诚地认为您在这里正在进行一场徒劳的战斗。您需要可能探索其他处理数据的方法,而不是将所有内容读入内存中。
如果我理解正确,您有多个线程同时处理数据,这就是为什么您不想直接从文件中处理,因为我认为这会导致I/O争用。
您是否考虑过或者是否有可能读取一块数据到内存中,让线程处理该块,然后读取下一块或者线程处理下一块?这样,在任何时候,您都不会将超过一块的数据保存在内存中,但是所有线程都可以访问该块。虽然这并不是最优解,但我认为这是一个起点。如果这是可行的,那么可以探索优化此方案的选项。
更新:使用平台调用分配非托管内存并从.NET中使用它的示例。
既然您如此确定需要将这么多数据加载到内存中,我想编写一个小的测试应用程序来验证这将起作用。为此,您将需要以下内容。
- 使用
/unsafe
编译选项进行编译。
- 如果您想要分配超过2GB的内存,还需要将目标平台切换为x64。
*上面第2点有些复杂,在64位操作系统上,您仍然可以针对x86
平台并获得完整的4 GB
内存访问权限。这将需要您使用类似EDITBIN.EXE的工具,在PE头中设置LargeAddressAware
标志。
这段代码使用
VirtualAllocEx 来分配非托管内存,并使用 .NET 流的隐喻
UnmanagedMemoryStream 来访问非托管内存。请注意,这段代码只经过一些非常基本的快速测试,在目标64位环境下进行,并且只使用了4GB RAM。最重要的是,该进程的内存利用率只到达了约2.6GB。
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.ComponentModel;
namespace MemoryMappedFileTests
{
class Program
{
static void Main(string[] args)
{
IntPtr ptr = IntPtr.Zero;
try
{
ptr = VirtualAllocEx(
Process.GetCurrentProcess().Handle,
IntPtr.Zero,
new IntPtr(0xD0000000L),
AllocationType.Commit | AllocationType.Reserve,
MemoryProtection.ReadWrite);
if (ptr == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
MEMORY_BASIC_INFORMATION mbi = new MEMORY_BASIC_INFORMATION();
IntPtr result = VirtualQueryEx(
Process.GetCurrentProcess().Handle,
ptr,
out mbi,
new IntPtr(Marshal.SizeOf(mbi)));
if (result == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
unsafe
{
byte* pBytes = (byte*)ptr.ToPointer();
UnmanagedMemoryStream stm = new UnmanagedMemoryStream(
pBytes,
mbi.RegionSize.ToInt64(),
mbi.RegionSize.ToInt64(),
FileAccess.ReadWrite);
StreamWriter sw = new StreamWriter(stm);
sw.Write("Everything seems to be working!\r\n");
sw.Flush();
stm.Position = 0;
StreamReader rd = new StreamReader(stm);
Console.WriteLine(rd.ReadLine());
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
if (ptr != IntPtr.Zero)
{
VirtualFreeEx(
Process.GetCurrentProcess().Handle,
ptr,
IntPtr.Zero,
FreeType.Release);
}
}
Console.ReadKey();
}
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr VirtualAllocEx(
IntPtr hProcess,
IntPtr lpAddress,
IntPtr dwSize,
AllocationType dwAllocationType,
MemoryProtection flProtect);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool VirtualFreeEx(
IntPtr hProcess,
IntPtr lpAddress,
IntPtr dwSize,
FreeType dwFreeType);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr VirtualQueryEx(
IntPtr hProcess,
IntPtr lpAddress,
out MEMORY_BASIC_INFORMATION lpBuffer,
IntPtr dwLength);
[StructLayout(LayoutKind.Sequential)]
public struct MEMORY_BASIC_INFORMATION
{
public IntPtr BaseAddress;
public IntPtr AllocationBase;
public int AllocationProtect;
public IntPtr RegionSize;
public int State;
public int Protect;
public int Type;
}
[Flags]
public enum AllocationType
{
Commit = 0x1000,
Reserve = 0x2000,
Decommit = 0x4000,
Release = 0x8000,
Reset = 0x80000,
Physical = 0x400000,
TopDown = 0x100000,
WriteWatch = 0x200000,
LargePages = 0x20000000
}
[Flags]
public enum MemoryProtection
{
Execute = 0x10,
ExecuteRead = 0x20,
ExecuteReadWrite = 0x40,
ExecuteWriteCopy = 0x80,
NoAccess = 0x01,
ReadOnly = 0x02,
ReadWrite = 0x04,
WriteCopy = 0x08,
GuardModifierflag = 0x100,
NoCacheModifierflag = 0x200,
WriteCombineModifierflag = 0x400
}
[Flags]
public enum FreeType
{
Decommit = 0x4000,
Release = 0x8000
}
}
}