mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-24 20:34:29 +08:00
refactor(store): 添加调试断言确保线程安全
- 在 CreateMiddlewareSnapshotCore 方法中添加锁持有检查 - 在 CreateReducerSnapshotCore 方法中添加锁持有检查 - 确保并发安全性避免竞态条件 - 导入 System.Diagnostics 命名空间支持断言功能
This commit is contained in:
parent
91246ff482
commit
bd29475748
@ -157,6 +157,48 @@ public sealed class Store<TState> : IStore<TState>, IStoreDiagnostics<TState>
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取最近一次分发的 action 类型。
|
||||
/// </summary>
|
||||
public Type? LastActionType
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _lastActionType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取最近一次真正改变状态的时间戳。
|
||||
/// </summary>
|
||||
public DateTimeOffset? LastStateChangedAt
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _lastStateChangedAt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前历史快照列表。
|
||||
/// </summary>
|
||||
public IReadOnlyList<StoreHistoryEntry<TState>> HistoryEntries
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _history.Count == 0 ? Array.Empty<StoreHistoryEntry<TState>>() : _history.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前状态快照。
|
||||
/// </summary>
|
||||
@ -278,54 +320,6 @@ public sealed class Store<TState> : IStore<TState>, IStoreDiagnostics<TState>
|
||||
NotifyListeners(listenersSnapshot, notificationState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将多个状态操作合并到一个批处理中执行。
|
||||
/// 批处理内的状态变化会立即提交,但通知会在最外层批处理结束后折叠为一次最终回放。
|
||||
/// </summary>
|
||||
/// <param name="batchAction">批处理主体。</param>
|
||||
/// <exception cref="ArgumentNullException">当 <paramref name="batchAction"/> 为 <see langword="null"/> 时抛出。</exception>
|
||||
public void RunInBatch(Action batchAction)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(batchAction);
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_batchDepth++;
|
||||
}
|
||||
|
||||
Action<TState>[] listenersSnapshot = Array.Empty<Action<TState>>();
|
||||
TState notificationState = default!;
|
||||
|
||||
try
|
||||
{
|
||||
batchAction();
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_batchDepth == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Batch depth is already zero.");
|
||||
}
|
||||
|
||||
_batchDepth--;
|
||||
if (_batchDepth == 0 && _hasPendingBatchNotification)
|
||||
{
|
||||
notificationState = _pendingBatchState;
|
||||
_pendingBatchState = default!;
|
||||
_hasPendingBatchNotification = false;
|
||||
listenersSnapshot = SnapshotListenersForNotification(notificationState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (listenersSnapshot.Length > 0)
|
||||
{
|
||||
NotifyListeners(listenersSnapshot, notificationState);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将当前状态回退到上一个历史点。
|
||||
/// </summary>
|
||||
@ -375,6 +369,134 @@ public sealed class Store<TState> : IStore<TState>, IStoreDiagnostics<TState>
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前订阅者数量。
|
||||
/// </summary>
|
||||
public int SubscriberCount
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _listeners.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取最近一次分发记录。
|
||||
/// </summary>
|
||||
public StoreDispatchRecord<TState>? LastDispatchRecord
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _lastDispatchRecord;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前 Store 使用的 action 匹配策略。
|
||||
/// </summary>
|
||||
public StoreActionMatchingMode ActionMatchingMode => _actionMatchingMode;
|
||||
|
||||
/// <summary>
|
||||
/// 获取历史缓冲区容量。
|
||||
/// </summary>
|
||||
public int HistoryCapacity => _historyCapacity;
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前可见历史记录数量。
|
||||
/// </summary>
|
||||
public int HistoryCount
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _history.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前状态在历史缓冲区中的索引。
|
||||
/// </summary>
|
||||
public int HistoryIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _historyIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前是否处于批处理阶段。
|
||||
/// </summary>
|
||||
public bool IsBatching
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _batchDepth > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将多个状态操作合并到一个批处理中执行。
|
||||
/// 批处理内的状态变化会立即提交,但通知会在最外层批处理结束后折叠为一次最终回放。
|
||||
/// </summary>
|
||||
/// <param name="batchAction">批处理主体。</param>
|
||||
/// <exception cref="ArgumentNullException">当 <paramref name="batchAction"/> 为 <see langword="null"/> 时抛出。</exception>
|
||||
public void RunInBatch(Action batchAction)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(batchAction);
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_batchDepth++;
|
||||
}
|
||||
|
||||
Action<TState>[] listenersSnapshot = Array.Empty<Action<TState>>();
|
||||
TState notificationState = default!;
|
||||
|
||||
try
|
||||
{
|
||||
batchAction();
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_batchDepth == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Batch depth is already zero.");
|
||||
}
|
||||
|
||||
_batchDepth--;
|
||||
if (_batchDepth == 0 && _hasPendingBatchNotification)
|
||||
{
|
||||
notificationState = _pendingBatchState;
|
||||
_pendingBatchState = default!;
|
||||
_hasPendingBatchNotification = false;
|
||||
listenersSnapshot = SnapshotListenersForNotification(notificationState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (listenersSnapshot.Length > 0)
|
||||
{
|
||||
NotifyListeners(listenersSnapshot, notificationState);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 订阅状态变化通知。
|
||||
/// </summary>
|
||||
@ -476,128 +598,6 @@ public sealed class Store<TState> : IStore<TState>, IStoreDiagnostics<TState>
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取最近一次分发的 action 类型。
|
||||
/// </summary>
|
||||
public Type? LastActionType
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _lastActionType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取最近一次真正改变状态的时间戳。
|
||||
/// </summary>
|
||||
public DateTimeOffset? LastStateChangedAt
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _lastStateChangedAt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前订阅者数量。
|
||||
/// </summary>
|
||||
public int SubscriberCount
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _listeners.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取最近一次分发记录。
|
||||
/// </summary>
|
||||
public StoreDispatchRecord<TState>? LastDispatchRecord
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _lastDispatchRecord;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前 Store 使用的 action 匹配策略。
|
||||
/// </summary>
|
||||
public StoreActionMatchingMode ActionMatchingMode => _actionMatchingMode;
|
||||
|
||||
/// <summary>
|
||||
/// 获取历史缓冲区容量。
|
||||
/// </summary>
|
||||
public int HistoryCapacity => _historyCapacity;
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前可见历史记录数量。
|
||||
/// </summary>
|
||||
public int HistoryCount
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _history.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前状态在历史缓冲区中的索引。
|
||||
/// </summary>
|
||||
public int HistoryIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _historyIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前历史快照列表。
|
||||
/// </summary>
|
||||
public IReadOnlyList<StoreHistoryEntry<TState>> HistoryEntries
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _history.Count == 0 ? Array.Empty<StoreHistoryEntry<TState>>() : _history.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前是否处于批处理阶段。
|
||||
/// </summary>
|
||||
public bool IsBatching
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _batchDepth > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个用于当前状态类型的 Store 构建器。
|
||||
/// </summary>
|
||||
@ -915,6 +915,10 @@ public sealed class Store<TState> : IStore<TState>, IStoreDiagnostics<TState>
|
||||
/// <returns>当前中间件链的快照;若未注册则返回空数组。</returns>
|
||||
private IStoreMiddleware<TState>[] CreateMiddlewareSnapshotCore()
|
||||
{
|
||||
Debug.Assert(
|
||||
Monitor.IsEntered(_lock),
|
||||
"Caller must hold _lock before invoking CreateMiddlewareSnapshotCore to avoid concurrency bugs.");
|
||||
|
||||
if (_middlewares.Count == 0)
|
||||
{
|
||||
return Array.Empty<IStoreMiddleware<TState>>();
|
||||
@ -937,6 +941,10 @@ public sealed class Store<TState> : IStore<TState>, IStoreDiagnostics<TState>
|
||||
/// <returns>对应 action 类型的 reducer 快照;若未注册则返回空数组。</returns>
|
||||
private IStoreReducerAdapter[] CreateReducerSnapshotCore(Type actionType)
|
||||
{
|
||||
Debug.Assert(
|
||||
Monitor.IsEntered(_lock),
|
||||
"Caller must hold _lock before invoking CreateReducerSnapshotCore to avoid concurrency bugs.");
|
||||
|
||||
if (_actionMatchingMode == StoreActionMatchingMode.ExactTypeOnly)
|
||||
{
|
||||
if (!_reducers.TryGetValue(actionType, out var exactReducers) || exactReducers.Count == 0)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user