diff --git a/GFramework.Core/coroutine/CoroutineScheduler.cs b/GFramework.Core/coroutine/CoroutineScheduler.cs index 18e5c2a..eb7e04a 100644 --- a/GFramework.Core/coroutine/CoroutineScheduler.cs +++ b/GFramework.Core/coroutine/CoroutineScheduler.cs @@ -35,6 +35,7 @@ public sealed class CoroutineScheduler( /// /// 协程异常处理回调,当协程执行过程中发生异常时触发 + /// 注意:事件处理程序会在独立任务中异步调用,以避免阻塞调度器主循环 /// public event Action? OnCoroutineException; @@ -386,15 +387,22 @@ public sealed class CoroutineScheduler( var slot = _slots[slotIndex]; var handle = slot?.Handle ?? default; - try + // 将异常回调派发到线程池,避免阻塞调度器主循环 + var handler = OnCoroutineException; + if (handler != null) { - // 触发异常回调 - OnCoroutineException?.Invoke(handle, ex); - } - catch (Exception callbackEx) - { - // 防止回调异常导致调度器崩溃 - Console.Error.WriteLine($"[CoroutineScheduler] Exception in error callback: {callbackEx}"); + Task.Run(() => + { + try + { + handler(handle, ex); + } + catch (Exception callbackEx) + { + // 防止回调异常传播,记录到控制台 + Console.Error.WriteLine($"[CoroutineScheduler] Exception in error callback: {callbackEx}"); + } + }); } // 输出到控制台作为后备 diff --git a/GFramework.Core/logging/appenders/AsyncLogAppender.cs b/GFramework.Core/logging/appenders/AsyncLogAppender.cs index fa35d73..fd78a75 100644 --- a/GFramework.Core/logging/appenders/AsyncLogAppender.cs +++ b/GFramework.Core/logging/appenders/AsyncLogAppender.cs @@ -10,9 +10,11 @@ public sealed class AsyncLogAppender : ILogAppender, IDisposable { private readonly Channel _channel; private readonly CancellationTokenSource _cts; + private readonly SemaphoreSlim _flushSemaphore = new(0, 1); private readonly ILogAppender _innerAppender; private readonly Task _processingTask; private bool _disposed; + private volatile bool _flushRequested; /// /// 创建异步日志输出器 @@ -74,6 +76,7 @@ public sealed class AsyncLogAppender : ILogAppender, IDisposable } _cts.Dispose(); + _flushSemaphore.Dispose(); _disposed = true; } @@ -92,38 +95,46 @@ public sealed class AsyncLogAppender : ILogAppender, IDisposable /// /// 刷新缓冲区(ILogAppender 接口实现) + /// 注意:此方法会阻塞直到所有待处理日志写入完成,或超时(默认30秒) + /// 超时结果可通过 OnFlushCompleted 事件观察 /// void ILogAppender.Flush() { - Flush(); + var success = Flush(); + OnFlushCompleted?.Invoke(success); } + /// + /// Flush 操作完成事件,参数指示是否成功(true)或超时(false) + /// + public event Action? OnFlushCompleted; + /// /// 刷新缓冲区,等待所有日志写入完成 + /// 使用信号量机制确保可靠的完成通知,避免竞态条件 /// /// 超时时间(默认30秒) - /// 是否成功刷新所有日志 + /// 是否成功刷新所有日志(true=成功,false=超时) public bool Flush(TimeSpan? timeout = null) { if (_disposed) return false; var actualTimeout = timeout ?? TimeSpan.FromSeconds(30); - var startTime = DateTime.UtcNow; - // 使用 SpinWait 替代忙轮询,更高效 - var spinWait = new SpinWait(); - while (_channel.Reader.Count > 0) + // 请求刷新 + _flushRequested = true; + + try { - if (DateTime.UtcNow - startTime > actualTimeout) - { - return false; // 超时 - } - - spinWait.SpinOnce(); + // 等待处理任务发出完成信号 + var success = _flushSemaphore.Wait(actualTimeout); + OnFlushCompleted?.Invoke(success); + return success; + } + finally + { + _flushRequested = false; } - - _innerAppender.Flush(); - return true; } /// @@ -144,6 +155,18 @@ public sealed class AsyncLogAppender : ILogAppender, IDisposable // 记录内部错误到控制台(避免递归) await Console.Error.WriteLineAsync($"[AsyncLogAppender] Error processing log entry: {ex.Message}"); } + + // 检查是否有刷新请求且通道已空 + if (_flushRequested && _channel.Reader.Count == 0) + { + _innerAppender.Flush(); + + // 发出完成信号 + if (_flushSemaphore.CurrentCount == 0) + { + _flushSemaphore.Release(); + } + } } } catch (OperationCanceledException) diff --git a/GFramework.Core/property/BindableProperty.cs b/GFramework.Core/property/BindableProperty.cs index 7034e72..d3e69bd 100644 --- a/GFramework.Core/property/BindableProperty.cs +++ b/GFramework.Core/property/BindableProperty.cs @@ -67,7 +67,10 @@ public class BindableProperty(T defaultValue = default!) : IBindableProperty< /// 新的属性值 public void SetValueWithoutEvent(T newValue) { - MValue = newValue; + lock (_lock) + { + MValue = newValue; + } } /// @@ -155,7 +158,10 @@ public class BindableProperty(T defaultValue = default!) : IBindableProperty< /// 当前属性值 protected virtual T GetValue() { - return MValue; + lock (_lock) + { + return MValue; + } } ///