优化 DbConfigProvider 的并发处理

在 DbConfigProvider 类中引入了 `_saveLock` 对象,以防止保存操作与定时刷新操作之间的冲突。更新了 `SaveAll` 方法以使用锁定机制,确保在保存配置时不受干扰,并在保存后更新缓存。同时,`DoRefresh` 方法现在使用 `Monitor.TryEnter` 来避免在保存期间阻塞刷新操作,并在检测到配置改变时重新加载配置并更新缓存,确保操作完成后释放锁。
This commit is contained in:
qcjxberin 2025-05-27 23:45:50 +08:00
parent 6f8a919a7a
commit 8d07a8cf79
1 changed files with 60 additions and 27 deletions

View File

@ -26,6 +26,9 @@ public class DbConfigProvider : ConfigProvider
public Int32 Period { get; set; } = 15;
private IDictionary<String, Object?>? _cache;
/// <summary>保存操作锁,防止与定时刷新冲突</summary>
private readonly Object _saveLock = new();
#endregion
#region
@ -215,13 +218,26 @@ public class DbConfigProvider : ConfigProvider
/// <summary>保存配置树到数据源</summary>
public override Boolean SaveAll()
{
var list = Parameter.FindAllByUserID(UserId, Category);
Save(list, Root, null);
lock (_saveLock)
{
XTrace.WriteLine("[{0}/{1}]开始保存配置", Category, UserId);
// 通知绑定对象,配置数据有改变
NotifyChange();
var list = Parameter.FindAllByUserID(UserId, Category);
Save(list, Root, null);
return true;
// 重新加载配置以更新缓存避免被DoRefresh覆盖
var dic = GetAll();
if (dic != null)
{
SaveCache(dic);
XTrace.WriteLine("[{0}/{1}]配置保存完成,已更新缓存", Category, UserId);
}
// 通知绑定对象,配置数据有改变
NotifyChange();
return true;
}
}
void Save(IList<Parameter> list, IConfigSection root, String? prefix)
@ -253,6 +269,9 @@ public class DbConfigProvider : ConfigProvider
pi.Save();
}
}
// 确保所有数据库操作完成,避免事务延迟导致的读取不一致
Thread.Sleep(10);
}
#endregion
@ -290,35 +309,49 @@ public class DbConfigProvider : ConfigProvider
/// <param name="state"></param>
protected void DoRefresh(Object state)
{
using var showSql = Parameter.Meta.Session.Dal.Session.SetShowSql(false);
var dic = GetAll();
if (dic == null) return;
var changed = new Dictionary<String, Object?>();
if (_cache != null)
// 使用TryEnter避免在保存操作期间阻塞
if (!Monitor.TryEnter(_saveLock, 100))
{
foreach (var item in dic)
{
// 跳过备注
if (item.Key[0] == '#') continue;
if (!_cache.TryGetValue(item.Key, out var v) || v + "" != item.Value + "")
{
changed.Add(item.Key, item.Value);
}
}
// 如果无法获取锁,说明正在保存,跳过本次刷新
return;
}
if (changed.Count > 0)
try
{
XTrace.WriteLine("[{0}/{1}]配置改变,重新加载如下键:{2}", Category, UserId, changed.ToJson());
using var showSql = Parameter.Meta.Session.Dal.Session.SetShowSql(false);
Root = Build(dic);
var dic = GetAll();
if (dic == null) return;
// 缓存
SaveCache(dic);
var changed = new Dictionary<String, Object?>();
if (_cache != null)
{
foreach (var item in dic)
{
// 跳过备注
if (item.Key[0] == '#') continue;
if (!_cache.TryGetValue(item.Key, out var v) || v + "" != item.Value + "")
{
changed.Add(item.Key, item.Value);
}
}
}
NotifyChange();
if (changed.Count > 0)
{
XTrace.WriteLine("[{0}/{1}]定时检测到配置改变,重新加载如下键:{2}", Category, UserId, changed.ToJson());
Root = Build(dic);
// 缓存
SaveCache(dic);
NotifyChange();
}
}
finally
{
Monitor.Exit(_saveLock);
}
}
#endregion