进程与共享缓存 - Snet Docs

进程缓存与共享缓存

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


缓存类型

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

ProcessCacheOperate(内存缓存)

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

何时使用

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

自 26.250.1 起,构造函数在 basics 为 null 时抛 ArgumentNullException,在 AbsoluteExpirationSlidingExpiration 小于等于 0 时抛 ArgumentOutOfRangeExceptionSetCacheAsync / GetCacheAsync / RemoveCacheAsync 在键为空时抛 ArgumentException,释放后的任何操作抛 ObjectDisposedExceptionDispose 直接释放底层 MemoryCache(不再清空)。

示例

// 创建或获取单例实例
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 应用程序之间共享状态
  • 需要进程隔离的生产场景

自 26.250.1 起,构造函数会校验配置(ShareCacheData):Path/MapName 非空、Name 必须为纯文件名(不含目录片段)、IndexSize > 0WholeSize > IndexSizeAccess 必须为 ReadWrite,否则抛 ArgumentException / ArgumentOutOfRangeException。初始化及每次读写/删除均持有跨进程命名互斥量(30 秒超时)与进程内门闩,索引在同一锁下刷新。SetCacheAsync / GetCacheAsync / RemoveCacheAsync 在键为空时抛 ArgumentException,释放后抛 ObjectDisposedExceptionGetCacheAsyncResultData 为原始 byte[]

示例

// 创建或获取共享缓存实例
var shareData = new ShareCacheData
{
    Path = System.IO.Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
        "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("shared_key");  // ResultData 直接为 byte[]
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
    });
}