进程缓存与共享缓存
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
});
}
