进程与共享缓存 - Snet Docs

进程缓存与共享缓存

Snet 为设备数据提供两种缓存策略:进程内内存缓存和共享内存映射文件缓存。


缓存类型

缓存 后端 作用域 持久性
进程缓存 ProcessCacheOperate MemoryCache 单个进程 重启后丢失
共享缓存 ShareCacheOperate MemoryMappedFile 跨进程 重启后保留

ProcessCacheOperate(内存缓存)

使用 Microsoft.Extensions.Caching.Memory.MemoryCache 的进程内缓存。最快的选项,零 IPC 开销。

何时使用

  • 单个进程访问缓存数据
  • 可以重建的短生命周期数据
  • 需要最大读取性能

示例

// 创建或获取单例实例
var basics = new ProcessCacheData
{
    AbsoluteExpiration = 60,  // 绝对过期时间(分钟)
    SlidingExpiration = 20    // 滑动过期时间(分钟)
};
var cache = await ProcessCacheOperate.InstanceAsync(basics);

// 存储一个值
await cache.SetCacheAsync("key", myValue);

// 检索
var result = await cache.GetCacheAsync<MyType>("key");
if (result.Status)
    Console.WriteLine($"缓存命中: {result.ResultData}");

ShareCacheOperate(内存映射文件)

通过 Windows MemoryMappedFile(Windows)或 Linux 上的等效方式实现跨进程缓存。允许独立进程之间共享数据。

何时使用

  • 多个进程需要访问相同的缓存数据
  • 数据应在进程重启后保留
  • 在 Windows 服务和 GUI 应用程序之间共享状态
  • 需要进程隔离的生产场景

示例

// 创建或获取共享缓存实例
var shareData = new ShareCacheData
{
    Path = @"C:\ProgramData\Snet\Cache",
    Name = "Snet-Cache.dat",
    WholeSize = 10 * 1024 * 1024  // 10 MB
};
var shareCache = await ShareCacheOperate.InstanceAsync(shareData);

// 写入其他进程可访问的数据
var sharedBytes = Encoding.UTF8.GetBytes("跨进程数据");
await shareCache.SetCacheAsync("shared_key", sharedBytes);

// 另一个进程读取相同的数据
var result = await shareCache.GetCacheAsync<byte[]>("shared_key");
if (result.Status)
{
    var data = Encoding.UTF8.GetString((byte[])result.ResultData!);
    Console.WriteLine($"共享数据: {data}");
}

选择指南

需要跨进程共享数据?
    +--- 是 --> ShareCacheOperate(内存映射文件)
    +--- 否 --> 需要在重启后持久化?
                    +--- 是 --> ShareCacheOperate
                    +--- 否 --> ProcessCacheOperate(内存缓存)

决策矩阵

需求 ProcessCacheOperate ShareCacheOperate
多进程共享
重启后持久化
速度 最快(内存中) 快速(mmap)
跨平台 是(平台特定的 mmap)
内存开销 GC 管理的堆 共享内存区域
并发访问 内置 通过内存映射文件同步

常见使用模式

缓存频繁读取的设备值以减少总线流量:

var basics = new ProcessCacheData
{
    AbsoluteExpiration = 60,
    SlidingExpiration = 20
};
var cache = await ProcessCacheOperate.InstanceAsync(basics);

// 键:设备 + 寄存器,值:上次读取的值 + 时间戳
var cacheKey = $"{deviceSN}:{addressName}";

var cachedResult = await cache.GetCacheAsync<CachedValue>(cacheKey);
if (cachedResult.Status && cachedResult.ResultData is CachedValue cached
    && (DateTime.Now - cached.Timestamp).TotalSeconds < 5)
{
    return cached.Value;  // 返回缓存,跳过总线读取
}

// 缓存未命中:从设备读取
var result = await modbus.ReadAsync(address);
if (result.Status)
{
    await cache.SetCacheAsync(cacheKey, new CachedValue
    {
        Value = result.ResultData,
        Timestamp = DateTime.Now
    });
}