NavisworksTransport/src/Core/UIStateManager.cs
2025-10-21 18:48:27 +08:00

950 lines
33 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace NavisworksTransport.Core
{
/// <summary>
/// UI状态管理器 - 提供线程安全的UI更新机制
/// 解决Navisworks插件中的跨线程UI操作问题防止崩溃和死锁
/// </summary>
public class UIStateManager
{
#region
private static UIStateManager _instance;
private static readonly object _instanceLock = new object();
private readonly SynchronizationContext _uiContext;
private readonly object _operationLock = new object();
private readonly ConcurrentQueue<UIUpdateOperation> _updateQueue;
private readonly int _flushInterval = 50; // 50ms最小处理间隔与UI刷新同步
private volatile bool _isProcessing = false;
private volatile bool _isDisposed = false;
/// <summary>
/// UI操作默认超时时间毫秒- 60秒
/// 设置较长时间以应对程序性能异常导致的UI线程阻塞
/// </summary>
private const int DEFAULT_UI_TIMEOUT = 60000;
/// <summary>
/// 获取UIStateManager的单例实例
/// </summary>
public static UIStateManager Instance
{
get
{
if (_instance == null)
{
lock (_instanceLock)
{
if (_instance == null)
{
_instance = new UIStateManager();
}
}
}
return _instance;
}
}
/// <summary>
/// 获取当前是否在UI线程中
/// </summary>
public bool IsUIThread
{
get
{
if (_uiContext != null)
{
return SynchronizationContext.Current == _uiContext;
}
// 回退到Windows Forms检查
try
{
var activeForm = Form.ActiveForm;
if (activeForm != null)
{
return !activeForm.InvokeRequired;
}
return false;
}
catch
{
return false;
}
}
}
/// <summary>
/// 获取当前等待处理的UI更新操作数量
/// </summary>
public int PendingOperationsCount => _updateQueue.Count;
#endregion
#region
private UIStateManager()
{
try
{
// 尝试获取当前的SynchronizationContext
_uiContext = SynchronizationContext.Current;
// 在Navisworks插件环境中完全跳过WPF Dispatcher的创建
// 因为它可能与宿主环境冲突并导致崩溃
if (_uiContext == null)
{
LogManager.Info("当前没有SynchronizationContextUIStateManager将使用Windows Forms回退机制");
// 不尝试创建WPF Dispatcher直接使用null让后续代码使用Windows Forms
}
_updateQueue = new ConcurrentQueue<UIUpdateOperation>();
LogManager.Info("UIStateManager初始化完成");
}
catch (Exception ex)
{
LogManager.Error($"UIStateManager初始化失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 手动设置UI上下文通常在主线程初始化时调用
/// </summary>
/// <param name="context">UI线程的SynchronizationContext</param>
public void SetUIContext(SynchronizationContext context)
{
if (context == null)
{
LogManager.Warning("尝试设置空的SynchronizationContext");
return;
}
// 只有在当前没有上下文或者在UI线程中才允许设置
if (_uiContext == null || IsUIThread)
{
var oldContext = _uiContext;
var newContext = context;
LogManager.Info($"UIStateManager上下文更新: {oldContext?.GetType()?.Name} -> {newContext.GetType().Name}");
}
else
{
LogManager.Warning("只能在UI线程中设置SynchronizationContext");
}
}
#endregion
#region UI更新接口
/// <summary>
/// 线程安全的异步UI更新操作
/// </summary>
/// <typeparam name="T">返回值类型</typeparam>
/// <param name="operation">要执行的UI操作</param>
/// <param name="timeout">超时时间毫秒默认5000ms</param>
/// <returns>操作结果</returns>
public async Task<T> ExecuteUIUpdateAsync<T>(Func<T> operation, int timeout = DEFAULT_UI_TIMEOUT)
{
if (_isDisposed)
{
throw new ObjectDisposedException(nameof(UIStateManager));
}
if (operation == null)
{
throw new ArgumentNullException(nameof(operation));
}
// 如果已经在UI线程中直接执行
if (IsUIThread)
{
try
{
return operation();
}
catch (Exception ex)
{
LogManager.Error($"UI操作执行失败: {ex.Message}");
throw;
}
}
// 创建任务完成信号
var tcs = new TaskCompletionSource<T>();
var cancellationToken = new CancellationTokenSource(timeout);
// 设置超时处理
cancellationToken.Token.Register(() =>
{
if (!tcs.Task.IsCompleted)
{
tcs.TrySetException(new TimeoutException($"UI操作超时{timeout}ms"));
}
});
try
{
// 使用SynchronizationContext切换到UI线程
if (_uiContext != null)
{
_uiContext.Post(_ =>
{
try
{
if (!cancellationToken.Token.IsCancellationRequested)
{
var result = operation();
tcs.TrySetResult(result);
}
}
catch (Exception ex)
{
LogManager.Error($"UI线程操作异常: {ex.Message}");
tcs.TrySetException(ex);
}
}, null);
}
else
{
// 回退到Control.BeginInvoke
var mainForm = GetMainForm();
if (mainForm != null)
{
mainForm.BeginInvoke(new Action(() =>
{
try
{
if (!cancellationToken.Token.IsCancellationRequested)
{
var result = operation();
tcs.TrySetResult(result);
}
}
catch (Exception ex)
{
LogManager.Error($"UI线程操作异常Control.BeginInvoke: {ex.Message}");
tcs.TrySetException(ex);
}
}));
}
else
{
throw new InvalidOperationException("无法找到UI上下文来执行操作");
}
}
return await tcs.Task;
}
finally
{
// 延迟释放,给异步操作清理时间,避免"CancellationTokenSource 已释放"异常
_ = Task.Delay(100).ContinueWith(_ => cancellationToken.Dispose());
}
}
/// <summary>
/// 无返回值的异步UI更新操作
/// </summary>
/// <param name="operation">要执行的UI操作</param>
/// <param name="timeout">超时时间毫秒默认5000ms</param>
public async Task ExecuteUIUpdateAsync(Action operation, int timeout = DEFAULT_UI_TIMEOUT)
{
await ExecuteUIUpdateAsync<object>(() =>
{
operation();
return null;
}, timeout);
}
#endregion
#region UI更新接口
/// <summary>
/// 批量执行UI更新操作原子性保证
/// 所有操作要么全部成功,要么全部失败
/// </summary>
/// <param name="updates">要执行的UI更新操作数组</param>
/// <param name="timeout">超时时间毫秒默认10000ms</param>
public async Task ExecuteBatchUIUpdate(Action[] updates, int timeout = DEFAULT_UI_TIMEOUT)
{
if (updates == null || updates.Length == 0)
{
return;
}
await ExecuteUIUpdateAsync(() =>
{
var exceptions = new List<Exception>();
// 执行所有更新操作
foreach (var update in updates)
{
try
{
update?.Invoke();
}
catch (Exception ex)
{
exceptions.Add(ex);
LogManager.Error($"批量UI更新中的操作失败: {ex.Message}");
}
}
// 如果有任何异常,抛出聚合异常
if (exceptions.Count > 0)
{
throw new AggregateException("批量UI更新过程中发生错误", exceptions);
}
}, timeout);
}
/// <summary>
/// 批量执行UI更新操作可变参数版本
/// </summary>
/// <param name="updates">要执行的UI更新操作</param>
public async Task ExecuteBatchUIUpdate(params Action[] updates)
{
await ExecuteBatchUIUpdate(updates, DEFAULT_UI_TIMEOUT);
}
/// <summary>
/// 同步版本的批量UI更新操作用于强制同步处理
/// 不使用异步机制直接在当前线程中处理UI操作
/// </summary>
/// <param name="updates">要执行的UI更新操作数组</param>
/// <param name="timeout">超时时间毫秒默认5000ms</param>
public void ExecuteBatchUIUpdateSync(Action[] updates, int timeout = DEFAULT_UI_TIMEOUT)
{
if (updates == null || updates.Length == 0)
{
return;
}
LogManager.Info($"开始同步执行{updates.Length}个UI更新操作");
var exceptions = new List<Exception>();
// 如果已经在UI线程中直接执行
if (IsUIThread)
{
LogManager.Info("当前已在UI线程直接同步执行批量操作");
foreach (var update in updates)
{
try
{
update?.Invoke();
}
catch (Exception ex)
{
exceptions.Add(ex);
LogManager.Error($"同步批量UI更新中的操作失败: {ex.Message}");
}
}
}
else
{
// 使用同步方式切换到UI线程
var completed = false;
Exception syncException = null;
var resetEvent = new ManualResetEventSlim(false);
LogManager.Info("切换到UI线程同步执行批量操作");
if (_uiContext != null)
{
_uiContext.Send(_ =>
{
try
{
LogManager.Info($"SynchronizationContext中同步执行{updates.Length}个操作");
foreach (var update in updates)
{
try
{
update?.Invoke();
}
catch (Exception ex)
{
exceptions.Add(ex);
LogManager.Error($"同步批量UI更新中的操作失败: {ex.Message}");
}
}
completed = true;
}
catch (Exception ex)
{
syncException = ex;
}
finally
{
resetEvent.Set();
}
}, null);
}
else
{
// 回退到Control.Invoke
var mainForm = GetMainForm();
if (mainForm != null)
{
mainForm.Invoke(new Action(() =>
{
try
{
LogManager.Info($"Control.Invoke中同步执行{updates.Length}个操作");
foreach (var update in updates)
{
try
{
update?.Invoke();
}
catch (Exception ex)
{
exceptions.Add(ex);
LogManager.Error($"同步批量UI更新中的操作失败: {ex.Message}");
}
}
completed = true;
}
catch (Exception ex)
{
syncException = ex;
}
finally
{
resetEvent.Set();
}
}));
}
else
{
throw new InvalidOperationException("无法找到UI上下文来执行同步批量UI更新");
}
}
// 等待完成或超时
if (!resetEvent.Wait(timeout))
{
LogManager.Error($"同步批量UI更新执行超时{timeout}ms");
throw new TimeoutException($"同步批量UI更新执行超时{timeout}ms");
}
// 检查执行结果
if (syncException != null)
{
throw new Exception("同步批量UI更新执行失败", syncException);
}
if (!completed)
{
throw new InvalidOperationException("同步批量UI更新未正确完成");
}
}
// 如果有任何异常,抛出聚合异常
if (exceptions.Count > 0)
{
LogManager.Error($"同步批量UI更新过程中发生{exceptions.Count}个错误");
throw new AggregateException("同步批量UI更新过程中发生错误", exceptions);
}
LogManager.Info($"同步批量UI更新成功完成{updates.Length}个操作");
}
#endregion
#region
/// <summary>
/// 将UI更新操作加入队列用于批量处理
/// </summary>
/// <param name="operation">UI更新操作</param>
/// <param name="priority">操作优先级</param>
public void QueueUIUpdate(Action operation, UIUpdatePriority priority = UIUpdatePriority.Normal, bool forceSync = false)
{
if (_isDisposed || operation == null)
{
return;
}
var updateOperation = new UIUpdateOperation
{
Id = Guid.NewGuid(),
Operation = operation,
Priority = priority,
QueueTime = DateTime.UtcNow
};
_updateQueue.Enqueue(updateOperation);
// 如果当前没有在处理,根据参数选择处理方式
if (!_isProcessing)
{
if (forceSync || priority == UIUpdatePriority.Critical)
{
// 强制同步处理直接处理队列不依赖Task.Run
LogManager.Info($"强制同步处理UI队列优先级: {priority}, 队列长度: {_updateQueue.Count}");
try
{
ProcessQueuedUpdates(forcedSync: true).GetAwaiter().GetResult();
}
catch (Exception ex)
{
LogManager.Error($"强制同步处理队列失败: {ex.Message}");
// 回退到异步处理
_ = Task.Run(() => ProcessQueuedUpdates(forcedSync: false));
EnsureIdleProcessorRunning();
}
}
else
{
// 普通异步处理 + 保底定时器
_ = Task.Run(() => ProcessQueuedUpdates(forcedSync: false));
EnsureIdleProcessorRunning();
}
}
}
/// <summary>
/// 强制同步处理UI更新操作便捷方法
/// 操作会先入队,然后立即同步处理整个队列
/// </summary>
/// <param name="operation">UI更新操作</param>
/// <param name="priority">操作优先级</param>
/// <param name="operationType">操作类型描述,用于日志记录</param>
public void QueueUIUpdateWithForcedSync(Action operation, UIUpdatePriority priority = UIUpdatePriority.High, string operationType = "强制同步UI更新")
{
LogManager.Info($"*** 开始{operationType},优先级: {priority} ***");
QueueUIUpdate(operation, priority, forceSync: true);
LogManager.Info($"*** {operationType}完成 ***");
}
/// <summary>
/// 强制刷新当前队列中的所有操作(不添加新操作)
/// 直接同步处理现有队列代替Task.Run
/// </summary>
/// <param name="operationType">操作类型描述,用于日志记录</param>
public void ForceFlushQueue(string operationType = "强制刷新队列")
{
if (_updateQueue.IsEmpty)
{
LogManager.Info($"{operationType}: 队列为空,无需处理");
return;
}
if (_isProcessing)
{
LogManager.Info($"{operationType}: 已在处理中,跳过");
return;
}
LogManager.Info($"*** 开始{operationType},当前队列长度: {_updateQueue.Count} ***");
try
{
ProcessQueuedUpdates(forcedSync: true).GetAwaiter().GetResult();
LogManager.Info($"*** {operationType}成功完成 ***");
}
catch (Exception ex)
{
LogManager.Error($"*** {operationType}失败: {ex.Message} ***");
throw;
}
}
/// <summary>
/// 立即执行UI更新操作绕过队列机制
/// 用于关键UI事件如RouteGenerated需要立即响应的场景
/// </summary>
/// <param name="operation">要执行的UI操作</param>
/// <param name="operationType">操作类型描述,用于日志记录</param>
/// <param name="timeout">超时时间毫秒默认2000ms</param>
public void ExecuteImmediateUIUpdate(Action operation, string operationType = "立即UI更新", int timeout = DEFAULT_UI_TIMEOUT)
{
if (_isDisposed || operation == null)
{
LogManager.Warning($"跳过{operationType}UIStateManager已释放或操作为null");
return;
}
try
{
LogManager.Info($"*** 开始立即执行{operationType} ***");
// 如果已经在UI线程中直接执行
if (IsUIThread)
{
LogManager.Info($"*** 当前已在UI线程直接执行{operationType} ***");
operation();
LogManager.Info($"*** {operationType}立即执行完成 ***");
return;
}
// 创建同步等待信号
var completed = false;
Exception exception = null;
var resetEvent = new ManualResetEventSlim(false);
LogManager.Info($"*** 切换到UI线程执行{operationType} ***");
// 使用同步方式切换到UI线程
if (_uiContext != null)
{
_uiContext.Send(_ =>
{
try
{
LogManager.Info($"*** SynchronizationContext中执行{operationType} ***");
operation();
completed = true;
}
catch (Exception ex)
{
exception = ex;
LogManager.Error($"{operationType}在UI线程中执行失败: {ex.Message}");
}
finally
{
resetEvent.Set();
}
}, null);
}
else
{
// 回退到Control.Invoke
var mainForm = GetMainForm();
if (mainForm != null)
{
mainForm.Invoke(new Action(() =>
{
try
{
LogManager.Info($"*** Control.Invoke中执行{operationType} ***");
operation();
completed = true;
}
catch (Exception ex)
{
exception = ex;
LogManager.Error($"{operationType}在UI线程中执行失败Control.Invoke: {ex.Message}");
}
finally
{
resetEvent.Set();
}
}));
}
else
{
throw new InvalidOperationException($"无法找到UI上下文来执行{operationType}");
}
}
// 等待完成或超时
if (!resetEvent.Wait(timeout))
{
LogManager.Error($"*** {operationType}执行超时({timeout}ms ***");
throw new TimeoutException($"{operationType}执行超时({timeout}ms");
}
// 检查执行结果
if (exception != null)
{
throw new Exception($"{operationType}执行失败", exception);
}
if (!completed)
{
throw new InvalidOperationException($"{operationType}未正确完成");
}
LogManager.Info($"*** {operationType}立即执行成功完成 ***");
}
catch (Exception ex)
{
LogManager.Error($"*** {operationType}立即执行失败: {ex.Message} ***", ex);
throw;
}
}
/// <summary>
/// 确保Idle处理器在运行处理可能积压的UI操作
/// 作为队列处理的保底机制防止Task.Run失败导致的队列积压
/// </summary>
private void EnsureIdleProcessorRunning()
{
try
{
// 如果队列为空,则不需要启动
if (_updateQueue.IsEmpty)
{
return;
}
// 注册到IdleEventManager
const string taskId = "UIStateManager_QueueProcessor";
IdleEventManager.Instance.RegisterTask(
taskId,
ProcessQueueOnIdle,
_flushInterval, // 最小间隔50ms
10 // 高优先级UI更新很重要
);
}
catch (Exception ex)
{
LogManager.Error($"注册Idle处理器失败: {ex.Message}");
}
}
/// <summary>
/// Idle事件处理器 - 检查并处理积压的队列
/// </summary>
private void ProcessQueueOnIdle()
{
try
{
// 如果没有积压的操作取消注册Idle任务
if (_updateQueue.IsEmpty)
{
IdleEventManager.Instance.UnregisterTask("UIStateManager_QueueProcessor");
return;
}
// 如果当前正在处理,等待下次检查
if (_isProcessing)
{
return;
}
LogManager.Info($"Idle事件检测到队列积压({_updateQueue.Count}个操作),开始处理");
// 强制启动队列处理Idle事件使用异步处理
_ = Task.Run(() => ProcessQueuedUpdates(forcedSync: false));
}
catch (Exception ex)
{
LogManager.Error($"Idle队列处理失败: {ex.Message}");
}
}
/// <summary>
/// 停止Idle队列处理器
/// </summary>
private void StopIdleProcessor()
{
try
{
IdleEventManager.Instance.UnregisterTask("UIStateManager_QueueProcessor");
LogManager.Info("已停止Idle队列处理器");
}
catch (Exception ex)
{
LogManager.Error($"停止Idle队列处理器失败: {ex.Message}");
}
}
/// <summary>
/// 处理队列中的UI更新操作
/// </summary>
/// <param name="forcedSync">是否强制同步处理(不使用异步)</param>
private async Task ProcessQueuedUpdates(bool forcedSync = false)
{
lock (_operationLock)
{
if (_isProcessing)
{
return; // 防止重入
}
_isProcessing = true;
}
try
{
var operations = new List<UIUpdateOperation>();
// 收集当前队列中的所有操作
while (_updateQueue.TryDequeue(out var operation))
{
operations.Add(operation);
}
if (operations.Count == 0)
{
return;
}
// 按优先级排序
operations = operations.OrderByDescending(op => op.Priority).ToList();
LogManager.Info($"开始处理{operations.Count}个排队的UI更新操作强制同步: {forcedSync}");
// 批量执行操作
var actions = operations.Select(op => op.Operation).ToArray();
if (forcedSync)
{
// 使用同步版本的批量更新
LogManager.Info("使用同步方式处理UI更新队列");
ExecuteBatchUIUpdateSync(actions);
LogManager.Info($"同步处理{operations.Count}个UI更新操作完成");
}
else
{
// 使用异步版本的批量更新
LogManager.Info("使用异步方式处理UI更新队列");
await ExecuteBatchUIUpdate(actions);
LogManager.Info($"异步处理{operations.Count}个UI更新操作完成");
}
// 处理完成后,如果队列为空,停止保底定时器
if (_updateQueue.IsEmpty)
{
StopIdleProcessor();
}
}
catch (Exception ex)
{
LogManager.Error($"处理UI更新队列时发生错误: {ex.Message}");
}
finally
{
lock (_operationLock)
{
_isProcessing = false;
}
}
}
/// <summary>
/// 清空待处理的UI更新队列
/// </summary>
public void ClearUpdateQueue()
{
var count = 0;
while (_updateQueue.TryDequeue(out _))
{
count++;
}
if (count > 0)
{
LogManager.Info($"清空了{count}个待处理的UI更新操作");
}
}
#endregion
#region
/// <summary>
/// 获取主窗体用于线程调用
/// </summary>
/// <returns>主窗体或null</returns>
private Control GetMainForm()
{
try
{
// 尝试获取当前活动窗体
var activeForm = Form.ActiveForm;
if (activeForm != null)
{
return activeForm;
}
// 尝试获取应用程序主窗体
if (Application.OpenForms.Count > 0)
{
return Application.OpenForms[0];
}
return null;
}
catch (Exception ex)
{
LogManager.Warning($"获取主窗体失败: {ex.Message}");
return null;
}
}
#endregion
#region IDisposable实现
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
if (_isDisposed)
{
return;
}
_isDisposed = true;
// 停止并清理保底定时器
StopIdleProcessor();
// 清空队列
ClearUpdateQueue();
LogManager.Info("UIStateManager已释放");
}
#endregion
}
#region
/// <summary>
/// UI更新操作封装
/// </summary>
internal class UIUpdateOperation
{
public Guid Id { get; set; }
public Action Operation { get; set; }
public UIUpdatePriority Priority { get; set; }
public DateTime QueueTime { get; set; }
}
/// <summary>
/// UI更新操作优先级
/// </summary>
public enum UIUpdatePriority
{
/// <summary>
/// 低优先级 - 非关键UI更新
/// </summary>
Low = 0,
/// <summary>
/// 普通优先级 - 标准UI更新
/// </summary>
Normal = 1,
/// <summary>
/// 高优先级 - 重要UI更新
/// </summary>
High = 2,
/// <summary>
/// 关键优先级 - 关键UI更新最优先处理
/// </summary>
Critical = 3
}
#endregion
}