内存映射的文件
内存映射文件 允许:
- 访问文件
- 在不同的进程中 共享内存
使用场景
- 使用 FileMap,快速 随机 访问大文件
- 在不同的进程或任务之间 共享文件
- 在不同的进程或任务之间 共享内存
- 使用 访问器 直接从内存位置 进行读写
- 使用 流 进行读写
内存映射文件API
- 允许 使用 物理文件 或 共享内存
- 共享的内存 可以大于 可用的 物理内存
- 因此 需要一个 后备存储器
- 把系统的页面文件 作为 后备存储器
- 可以为 特定的 物理文件 或 共享内存 创建一个 内存映射文件
- 创建了内存映射之后,就可以创建一个视图
- 视图 用于 映射完整内存映射文件的一部分
- 访问视图,进行读写
- 可以给 内存映射 指定名称
- 使用名称,允许不同的进程访问 同一个 共享的内存
创建一个内存映射文件
MemoryMappedFile.CreateOrOpen()- 第一个参数:指定内存映射的名称
- 如果不存在就创建一个
- 第二个参数:内存映射文件的大小
- 第三个参数:所需的访问
MemoryMappedFile.OpenExisting()- 打开现有文件
MemoryMappedFile.CreateFromFile()- 访问物理文件
CreateViewAccessor() 从内存映射文件 创建一个 视图访问器 MemoryMappedViewAccessor
- 第一个参数:使用的偏移量
- 第二个参数:使用的大小
- 最大值 可以是 内存映射文件的 大小
- 第三个参数:指定用于读还是写
MemoryMappedFileAccess.Write
用 MemoryMappedViewAccessor 的 Write()方法,将内容 写入 共享内存
- 第一个参数:数据应该写入的位置
- 第二个参数:写入的内容
读取 共享内存里的内容
MemoryMappedFile.OpenExisting()- 第一个参数:内存映射文件的名称
- 第二个参数:读/写
- 从内存映射文件 创建一个 视图访问器
MemoryMappedViewAccessor - 从
MemoryMappedViewAccessor的Read()方法,读取 共享内存里的 内容- 第一个参数:数据的位置
示例
通过内存映射文件通信时,必须同步 读取器 和 写入器。这样读取器才知道数据何时可用。
private ManualResetEventSlim _mapCreated = new ManualResetEventSlim(initialState: false);
private ManualResetEventSlim _dataWrittenEvent = new ManualResetEventSlim(initialState: false);
private const string MAPNAME = "SampleMap";
public void Run()
{
Task.Run(() => WriterAsync());
Task.Run(() => Reader());
Console.WriteLine("tasks started");
}
private async Task WriterAsync()
{
try
{
using (MemoryMappedFile mappedFile = MemoryMappedFile.CreateOrOpen(MAPNAME, 10000, MemoryMappedFileAccess.ReadWrite))
// MemoryMappedFile mappedFile = MemoryMappedFile.CreateFromFile("./memoryMappedFile", FileMode.Create, MAPNAME, 10000);
{
_mapCreated.Set(); // signal shared memory segment created
Console.WriteLine("shared memory segment created");
using (MemoryMappedViewAccessor accessor = mappedFile.CreateViewAccessor(0, 10000, MemoryMappedFileAccess.Write))
{
for (int i = 0, pos = 0; i < 100; i++, pos += 4)
{
accessor.Write(pos, i);
Console.WriteLine($"written {i} at position {pos}");
await Task.Delay(10);
}
_dataWrittenEvent.Set(); // signal all data written
Console.WriteLine("data written");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"writer {ex.Message}");
}
}
private void Reader()
{
try
{
Console.WriteLine("reader");
_mapCreated.Wait();
Console.WriteLine("reader starting");
using (MemoryMappedFile mappedFile = MemoryMappedFile.OpenExisting(MAPNAME, MemoryMappedFileRights.Read))
{
using (MemoryMappedViewAccessor accessor = mappedFile.CreateViewAccessor(0, 10000, MemoryMappedFileAccess.Read))
{
_dataWrittenEvent.Wait();
Console.WriteLine("reading can start now");
for (int i = 0; i < 400; i += 4)
{
int result = accessor.ReadInt32(i);
Console.WriteLine($"reading {result} from position {i}");
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"reader {ex.Message}");
}
}
使用流 MemoryMappedViewStream
- 可以从
MemoryMappedFile调用CreateViewStream()创建一个MemoryMappedViewStream。- 其作用和用法类似于
MemoryMappedViewAccessor
- 其作用和用法类似于
- 用
MemoryMappedViewStream构造一个StreamWriter - 用
StreamWriter的WriteLineAsync()方法将字符串写入到 流 里StreamWriter会缓存写入操作,所以流的位置不是在每一个写入操作中都更新。而是只在写入块的时候 才更新- 为了让每个写入操作 流的位置 都更新,需要把
StreamWriter的AutoFlush属性设为true
- 用
MemoryMappedViewStream构造一个StreamReader - 用
StreamReader的ReadLineAsync()从共享内存中读取内容
private async Task WriterUsingStreamsAsync()
{
try
{
using (MemoryMappedFile mappedFile = MemoryMappedFile.CreateOrOpen(MAPNAME, 10000, MemoryMappedFileAccess.ReadWrite))
// MemoryMappedFile mappedFile = MemoryMappedFile.CreateFromFile("./memoryMappedFile", FileMode.Create, MAPNAME, 10000);
{
_mapCreated.Set(); // signal shared memory segment created
Console.WriteLine("shared memory segment created");
MemoryMappedViewStream stream = mappedFile.CreateViewStream(0, 10000, MemoryMappedFileAccess.Write);
using (var writer = new StreamWriter(stream))
{
writer.AutoFlush = true;
for (int i = 0; i < 100; i++)
{
string s = $"some data {i}";
Console.WriteLine($"writing {s} at {stream.Position}");
await writer.WriteLineAsync(s);
}
}
_dataWrittenEvent.Set(); // signal all data written
Console.WriteLine("data written");
}
}
catch (Exception ex)
{
Console.WriteLine($"writer {ex.Message}");
}
}
private async Task ReaderUsingStreamsAsync()
{
try
{
Console.WriteLine("reader");
_mapCreated.Wait();
Console.WriteLine("reader starting");
using (MemoryMappedFile mappedFile = MemoryMappedFile.OpenExisting(MAPNAME, MemoryMappedFileRights.Read))
{
MemoryMappedViewStream stream = mappedFile.CreateViewStream(0, 10000, MemoryMappedFileAccess.Read);
using (var reader = new StreamReader(stream))
{
_dataWrittenEvent.Wait();
Console.WriteLine("reading can start now");
for (int i = 0; i < 100; i++)
{
long pos = stream.Position;
string s = await reader.ReadLineAsync();
Console.WriteLine($"read {s} from {pos}");
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"reader {ex.Message}");
}
}