mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
fix(pause): 修复暂停管理器的并发安全和资源清理问题
- 添加了_disposed标志位用于跟踪对象销毁状态 - 实现了完整的DestroyAsync方法进行资源清理和数据结构清空 - 添加了ThrowIfDisposed方法防止对象销毁后被使用 - 将状态变更通知移到锁外部以避免死锁问题 - 在多个公共方法中添加了销毁状态检查 - 修复了ClearGroup和ClearAll方法中的死锁风险 - 改进了异常处理机制,确保线程安全
This commit is contained in:
parent
7734fba56f
commit
ccc3d046ca
@ -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>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user