fix(pause): 修复暂停管理器的并发安全和资源清理问题

- 添加了_disposed标志位用于跟踪对象销毁状态
- 实现了完整的DestroyAsync方法进行资源清理和数据结构清空
- 添加了ThrowIfDisposed方法防止对象销毁后被使用
- 将状态变更通知移到锁外部以避免死锁问题
- 在多个公共方法中添加了销毁状态检查
- 修复了ClearGroup和ClearAll方法中的死锁风险
- 改进了异常处理机制,确保线程安全
This commit is contained in:
GeWuYou 2026-03-02 21:33:00 +08:00 committed by gewuyou
parent 7734fba56f
commit ccc3d046ca

View File

@ -17,6 +17,7 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
private readonly ILogger _logger = LoggerFactoryResolver.Provider.CreateLogger(nameof(PauseStackManager)); private readonly ILogger _logger = LoggerFactoryResolver.Provider.CreateLogger(nameof(PauseStackManager));
private readonly Dictionary<PauseGroup, Stack<PauseEntry>> _pauseStacks = new(); private readonly Dictionary<PauseGroup, Stack<PauseEntry>> _pauseStacks = new();
private readonly Dictionary<Guid, PauseEntry> _tokenMap = new(); private readonly Dictionary<Guid, PauseEntry> _tokenMap = new();
private volatile bool _disposed;
/// <summary> /// <summary>
/// 异步销毁方法,在组件销毁时调用。 /// 异步销毁方法,在组件销毁时调用。
@ -24,7 +25,32 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
/// <returns>表示异步操作完成的任务。</returns> /// <returns>表示异步操作完成的任务。</returns>
public ValueTask DestroyAsync() public ValueTask DestroyAsync()
{ {
if (_disposed)
return ValueTask.CompletedTask;
_lock.EnterWriteLock();
try
{
if (_disposed)
return ValueTask.CompletedTask;
_disposed = true;
// 清理所有数据结构
_pauseStacks.Clear();
_tokenMap.Clear();
_handlers.Clear();
_logger.Debug("PauseStackManager destroyed");
}
finally
{
_lock.ExitWriteLock();
}
// 释放锁资源
_lock.Dispose(); _lock.Dispose();
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
} }
@ -41,9 +67,14 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
/// <returns>表示此次暂停请求的令牌。</returns> /// <returns>表示此次暂停请求的令牌。</returns>
public PauseToken Push(string reason, PauseGroup group = PauseGroup.Global) public PauseToken Push(string reason, PauseGroup group = PauseGroup.Global)
{ {
PauseToken token;
bool shouldNotify = false;
_lock.EnterWriteLock(); _lock.EnterWriteLock();
try try
{ {
ThrowIfDisposed();
var wasPaused = IsPausedInternal(group); var wasPaused = IsPausedInternal(group);
var entry = new PauseEntry var entry = new PauseEntry
@ -65,18 +96,23 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
_logger.Debug($"Pause pushed: {reason} (Group: {group}, Depth: {stack.Count})"); _logger.Debug($"Pause pushed: {reason} (Group: {group}, Depth: {stack.Count})");
// 状态变化检测:从未暂停 → 暂停 token = new PauseToken(entry.TokenId);
if (!wasPaused)
{
NotifyHandlers(group, true);
}
return new PauseToken(entry.TokenId); // 状态变化检测:从未暂停 → 暂停
shouldNotify = !wasPaused;
} }
finally finally
{ {
_lock.ExitWriteLock(); _lock.ExitWriteLock();
} }
// 在锁外通知处理器,避免死锁
if (shouldNotify)
{
NotifyHandlers(group, true);
}
return token;
} }
/// <summary> /// <summary>
@ -89,9 +125,15 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
if (!token.IsValid) if (!token.IsValid)
return false; return false;
bool found;
bool shouldNotify = false;
PauseGroup notifyGroup = PauseGroup.Global;
_lock.EnterWriteLock(); _lock.EnterWriteLock();
try try
{ {
ThrowIfDisposed();
if (!_tokenMap.TryGetValue(token.Id, out var entry)) if (!_tokenMap.TryGetValue(token.Id, out var entry))
{ {
_logger.Warn($"Attempted to pop invalid/expired token: {token.Id}"); _logger.Warn($"Attempted to pop invalid/expired token: {token.Id}");
@ -104,7 +146,7 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
// 从栈中移除 // 从栈中移除
var tempStack = new Stack<PauseEntry>(); var tempStack = new Stack<PauseEntry>();
bool found = false; found = false;
while (stack.Count > 0) while (stack.Count > 0)
{ {
@ -132,16 +174,23 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
// 状态变化检测:从暂停 → 未暂停 // 状态变化检测:从暂停 → 未暂停
if (wasPaused && stack.Count == 0) if (wasPaused && stack.Count == 0)
{ {
NotifyHandlers(group, false); shouldNotify = true;
notifyGroup = group;
} }
} }
return found;
} }
finally finally
{ {
_lock.ExitWriteLock(); _lock.ExitWriteLock();
} }
// 在锁外通知处理器,避免死锁
if (shouldNotify)
{
NotifyHandlers(notifyGroup, false);
}
return found;
} }
/// <summary> /// <summary>
@ -154,6 +203,7 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
_lock.EnterReadLock(); _lock.EnterReadLock();
try try
{ {
ThrowIfDisposed();
return IsPausedInternal(group); return IsPausedInternal(group);
} }
finally finally
@ -172,6 +222,7 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
_lock.EnterReadLock(); _lock.EnterReadLock();
try try
{ {
ThrowIfDisposed();
return _pauseStacks.TryGetValue(group, out var stack) ? stack.Count : 0; return _pauseStacks.TryGetValue(group, out var stack) ? stack.Count : 0;
} }
finally finally
@ -190,6 +241,8 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
_lock.EnterReadLock(); _lock.EnterReadLock();
try try
{ {
ThrowIfDisposed();
if (!_pauseStacks.TryGetValue(group, out var stack)) if (!_pauseStacks.TryGetValue(group, out var stack))
return Array.Empty<string>(); return Array.Empty<string>();
@ -209,6 +262,10 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
/// <returns>表示暂停作用域的 IDisposable 对象。</returns> /// <returns>表示暂停作用域的 IDisposable 对象。</returns>
public IDisposable PauseScope(string reason, PauseGroup group = PauseGroup.Global) public IDisposable PauseScope(string reason, PauseGroup group = PauseGroup.Global)
{ {
if (_disposed)
throw new ObjectDisposedException(nameof(PauseStackManager),
"Cannot use PauseStackManager after it has been destroyed");
return new PauseScope(this, reason, group); return new PauseScope(this, reason, group);
} }
@ -218,9 +275,13 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
/// <param name="group">要清空的暂停组。</param> /// <param name="group">要清空的暂停组。</param>
public void ClearGroup(PauseGroup group) public void ClearGroup(PauseGroup group)
{ {
bool shouldNotify = false;
_lock.EnterWriteLock(); _lock.EnterWriteLock();
try try
{ {
ThrowIfDisposed();
if (!_pauseStacks.TryGetValue(group, out var stack)) if (!_pauseStacks.TryGetValue(group, out var stack))
return; return;
@ -237,15 +298,18 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
_logger.Warn($"Cleared all pauses for group: {group}"); _logger.Warn($"Cleared all pauses for group: {group}");
// 状态变化检测 // 状态变化检测
if (wasPaused) shouldNotify = wasPaused;
{
NotifyHandlers(group, false);
}
} }
finally finally
{ {
_lock.ExitWriteLock(); _lock.ExitWriteLock();
} }
// 在锁外通知处理器,避免死锁
if (shouldNotify)
{
NotifyHandlers(group, false);
}
} }
/// <summary> /// <summary>
@ -253,10 +317,14 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
/// </summary> /// </summary>
public void ClearAll() public void ClearAll()
{ {
List<PauseGroup> pausedGroups;
_lock.EnterWriteLock(); _lock.EnterWriteLock();
try try
{ {
var pausedGroups = _pauseStacks ThrowIfDisposed();
pausedGroups = _pauseStacks
.Where(kvp => kvp.Value.Count > 0) .Where(kvp => kvp.Value.Count > 0)
.Select(kvp => kvp.Key) .Select(kvp => kvp.Key)
.ToList(); .ToList();
@ -265,17 +333,17 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
_tokenMap.Clear(); _tokenMap.Clear();
_logger.Warn("Cleared all pauses for all groups"); _logger.Warn("Cleared all pauses for all groups");
// 通知所有之前暂停的组
foreach (var group in pausedGroups)
{
NotifyHandlers(group, false);
}
} }
finally finally
{ {
_lock.ExitWriteLock(); _lock.ExitWriteLock();
} }
// 在锁外通知所有之前暂停的组,避免死锁
foreach (var group in pausedGroups)
{
NotifyHandlers(group, false);
}
} }
/// <summary> /// <summary>
@ -287,6 +355,8 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
_lock.EnterWriteLock(); _lock.EnterWriteLock();
try try
{ {
ThrowIfDisposed();
if (!_handlers.Contains(handler)) if (!_handlers.Contains(handler))
{ {
_handlers.Add(handler); _handlers.Add(handler);
@ -308,6 +378,8 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
_lock.EnterWriteLock(); _lock.EnterWriteLock();
try try
{ {
ThrowIfDisposed();
if (_handlers.Remove(handler)) if (_handlers.Remove(handler))
{ {
_logger.Debug($"Unregistered pause handler: {handler.GetType().Name}"); _logger.Debug($"Unregistered pause handler: {handler.GetType().Name}");
@ -319,6 +391,18 @@ public class PauseStackManager : AbstractContextUtility, IPauseStackManager, IAs
} }
} }
/// <summary>
/// 检查是否已销毁,如果已销毁则抛出异常
/// </summary>
private void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(PauseStackManager),
"Cannot use PauseStackManager after it has been destroyed");
}
}
/// <summary> /// <summary>
/// 内部查询暂停状态的方法,不加锁。 /// 内部查询暂停状态的方法,不加锁。
/// </summary> /// </summary>