优化 DbConfigProvider 的并发处理
在 DbConfigProvider 类中引入了 `_saveLock` 对象,以防止保存操作与定时刷新操作之间的冲突。更新了 `SaveAll` 方法以使用锁定机制,确保在保存配置时不受干扰,并在保存后更新缓存。同时,`DoRefresh` 方法现在使用 `Monitor.TryEnter` 来避免在保存期间阻塞刷新操作,并在检测到配置改变时重新加载配置并更新缓存,确保操作完成后释放锁。
This commit is contained in:
parent
6f8a919a7a
commit
8d07a8cf79
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue