欢迎光临
我们一直在努力

对 StartQuery 相关代码的进一步细化优化,重点聚焦于异步实现、定时器优化、特定硬件适配和性能调优

对 StartQuery 相关代码的进一步细化优化,重点聚焦于异步实现、定时器优化、特定硬件适配和性能调优。我将提供具体的异步版本代码,优化定时器机制,并考虑硬件适配的灵活性,同时结合性能调优策略。每个部分都会详细解释优化细节和实现逻辑,保持中文解释清晰易懂。


优化目标

  • 异步实现:将同步的硬件查询逻辑改为异步,使用 async/await 提高 IO 密集型操作的性能。

  • 定时器优化:替换 System.Timers.Timer 为更现代的 PeriodicTimer,支持异步操作并提供更灵活的控制。

  • 硬件适配:设计灵活的接口,适配不同硬件 API(同步/异步),并支持可配置的查询参数。

  • 性能调优:减少锁竞争、优化线程管理、降低 CPU 和内存开销。

  • 错误处理和日志:增强异常处理和日志记录,便于调试和监控。


  • 问题回顾

    • 当前问题:

      • StartQuery 使用两个线程(_StartQuery 和 _StartQueryAdapterBoard),但共享 HardwareMgr.DAQLocks[SectionId] 和静态 m_Locker,导致锁竞争和潜在死锁风险。

      • Thread.Sleep(15) 忙等待导致 CPU 占用高。

      • 同步调用 m_Hardware.QueryData 可能阻塞线程,限制性能。

      • 异常处理不统一,_StartQueryAdapterBoard 的空 catch 块可能隐藏错误。

    • 需求:

      • 异步实现以支持现代硬件 API。

      • 优化定时器机制以降低资源占用。

      • 适配不同硬件 API(同步/异步)。

      • 性能调优以提高吞吐量和响应性。


    优化方案

    1. 异步实现

    • 方案:将 m_Hardware.QueryData 和 m_Hardware.ReadData 改为异步方法,使用 SemaphoreSlim 替代 lock 支持异步锁。

    • 技术:使用 async/await 和 Task,确保非阻塞调用。

    • 好处:减少线程阻塞,提高 IO 密集型操作的性能。

    2. 定时器优化

    • 方案:使用 .NET 6+ 的 PeriodicTimer 替代 System.Timers.Timer,支持异步定时操作,配合 CancellationToken 提供优雅的停止机制。

    • 好处:更现代的 API,降低资源占用,支持异步操作,易于取消。

    3. 硬件适配

    • 方案:定义硬件访问接口(如 IHardware),支持同步和异步实现,允许动态配置查询指令和参数。

    • 好处:提高代码灵活性,适配不同硬件 API,方便扩展。

    4. 性能调优

    • 方案:

      • 合并 _StartQuery 和 _StartQueryAdapterBoard 为单一异步任务,减少线程开销。

      • 使用批量查询(如果硬件支持)减少硬件访问次数。

      • 优化状态管理,使用 volatile 或 Interlocked 确保线程安全。

    • 好处:降低上下文切换开销,减少锁竞争,提高吞吐量。

    5. 错误处理和日志

    • 方案:统一异常处理,添加详细日志,记录硬件状态和错误信息。

    • 好处:提高调试效率,增强系统健壮性。


    优化后的代码

    以下是完整的异步实现代码,包含定时器优化、硬件适配和性能调优。

    1. 硬件接口定义

    定义 IHardware 接口,支持同步和异步硬件访问。

    csharp

    public interface IHardware
    {
    bool Connected { get; }
    Task<HardwareResult> QueryDataAsync(string instruction, CancellationToken cancellationToken = default);
    Task<HardwareResult> ReadDataAsync(CancellationToken cancellationToken = default);
    }

    public class Hardware : IHardware
    {
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
    public bool Connected { get; set; } // 硬件连接状态

    public async Task<HardwareResult> QueryDataAsync(string instruction, CancellationToken cancellationToken = default)
    {
    await _semaphore.WaitAsync(cancellationToken);
    try
    {
    // 模拟异步硬件查询
    await Task.Delay(10, cancellationToken); // 替换为实际硬件 API
    return new HardwareResult(); // 示例实现
    }
    finally
    {
    _semaphore.Release();
    }
    }

    public async Task<HardwareResult> ReadDataAsync(CancellationToken cancellationToken = default)
    {
    await _semaphore.WaitAsync(cancellationToken);
    try
    {
    // 模拟异步读取
    await Task.Delay(10, cancellationToken); // 替换为实际硬件 API
    return new HardwareResult(); // 示例实现
    }
    finally
    {
    _semaphore.Release();
    }
    }
    }

    解释:

    • 接口设计:IHardware 定义异步方法,支持不同硬件实现。

    • 异步锁:使用 SemaphoreSlim 替代 lock,支持异步操作。

    • 取消支持:添加 CancellationToken 参数,允许中断硬件查询。

    2. 优化 StartQuery 和采集逻辑

    使用 PeriodicTimer 和异步方法,合并线程,统一采集逻辑。

    csharp

    private PeriodicTimer _queryTimer;
    private CancellationTokenSource _cts = new CancellationTokenSource();
    private volatile bool _isRunning = false;
    private volatile bool _firstRate = true;
    private volatile string _lastErrorCode = string.Empty;

    public async Task StartQueryAsync()
    {
    if (_isRunning)
    return; // 防止重复启动

    _isRunning = true;
    RunningStatus = RunningStatus.Running;

    // 初始化硬件
    if (!await InitializeHardwareAsync(_cts.Token))
    {
    await StopQueryAsync();
    return;
    }

    SystemNotifEvent.HardInfoNotifEvent(this, $"{Name}: 开始状态巡检", false);

    // 初始化 PeriodicTimer,100ms 间隔(可配置)
    _queryTimer = new PeriodicTimer(TimeSpan.FromMilliseconds(100));
    try
    {
    while (await _queryTimer.WaitForNextTickAsync(_cts.Token))
    {
    if (!_isRunning || !m_Hardware.Connected)
    {
    await StopQueryAsync();
    break;
    }
    try
    {
    await RateQueryAsync();
    await QueryAdapterBoardAsync();
    JudgMentBIBStatus();
    }
    catch (Exception ex)
    {
    HandleException(ex, "巡检状态异常");
    }
    }
    }
    catch (OperationCanceledException)
    {
    // 正常取消
    await StopQueryAsync();
    }
    catch (Exception ex)
    {
    HandleException(ex, "巡检循环异常");
    await StopQueryAsync();
    }
    }

    public async Task StopQueryAsync()
    {
    if (!_isRunning)
    return;

    _isRunning = false;
    _cts.Cancel();
    _queryTimer?.Dispose();
    _queryTimer = null;
    StoppedQuery = true;
    m_RateQuery = false;
    SystemNotifEvent.HardInfoNotifEvent(this, $"{Name}: 已退出状态巡检", false);
    _cts.Dispose();
    _cts = new CancellationTokenSource();
    }

    private async Task<bool> InitializeHardwareAsync(CancellationToken cancellationToken)
    {
    try
    {
    if (m_Hardware == null)
    {
    await InitializeAsync(cancellationToken); // 假设 Initialize 支持异步
    if (m_Hardware == null)
    {
    HandleException(new Exception("硬件未找到"), "未找到硬件");
    return false;
    }
    }
    if (!m_Hardware.Connected)
    {
    HandleException(new Exception("硬件未连接"), "硬件未连接");
    return false;
    }
    return true;
    }
    catch (Exception ex)
    {
    HandleException(ex, "硬件初始化失败");
    return false;
    }
    }

    private async Task RateQueryAsync()
    {
    try
    {
    if (m_TestStaus)
    return;

    HardwareResult hardwareResult = ReadLooping
    ? await m_Hardware.ReadDataAsync(_cts.Token)
    : await m_Hardware.QueryDataAsync("DAQREAD ", _cts.Token);

    bool changed = _lastErrorCode != hardwareResult.Error.ToString();
    _lastErrorCode = hardwareResult.Error.ToString();

    if (hardwareResult.Error.ToString() == "0000")
    {
    if (changed && HardwareConnectStatus != HardwareConnectStatus.InUsing)
    {
    HardwareConnectStatus = HardwareConnectStatus.InUsing;
    if (!_firstRate)
    {
    SystemNotifEvent.HardInfoNotifEvent(this, $"{Name} 采集板状态恢复正常。", true);
    }
    }
    BIBStatus = Convert.ToInt32(hardwareResult.ResultMap["BIBOnOffLineStatus"]);
    DAQStatus = Convert.ToInt32(hardwareResult.ResultMap["DAQStatus"]);
    }
    else if (changed)
    {
    ProcessError(hardwareResult, HardwareConnectStatus.InUsing);
    }
    }
    catch (Exception ex)
    {
    bool changed = LastExceptionMsg != ex.Message;
    if (changed)
    {
    LastExceptionMsg = ex.Message;
    HandleException(ex, "采集板巡检状态异常");
    }
    }
    finally
    {
    _firstRate = false;
    }
    }

    private async Task QueryAdapterBoardAsync()
    {
    try
    {
    HardwareResult resultAdapterBoard = await m_Hardware.QueryDataAsync("DAQREADADAPTERBOARD ", _cts.Token);
    if (resultAdapterBoard != null)
    {
    AdapterBoardTemp = Convert.ToDouble(resultAdapterBoard.ResultMap["AdapterBoardTemp"]);
    }
    }
    catch (Exception ex)
    {
    HandleException(ex, "适配板数据读取异常");
    }
    }

    private void HandleException(Exception ex, string message)
    {
    ExceptionDoEvnets(ex);
    m_Log.Error($"{Name}: {message}: {ex.Message}");
    SystemNotifEvent.HardErrorNotifEvent(this, $"{Name}: {message}", "");
    }

    解释:

    • 异步实现:所有硬件访问方法(QueryDataAsync, ReadDataAsync)都使用 async/await,支持非阻塞操作。

    • PeriodicTimer:使用 .NET 6+ 的 PeriodicTimer,支持异步定时,配合 CancellationToken 提供优雅取消机制。

    • 合并线程:将主板和适配板采集合并为单一异步任务,减少线程开销。

    • 取消支持:通过 _cts 实现采集任务的取消,确保优雅停止。

    • 硬件初始化:提取 InitializeHardwareAsync,支持异步初始化,减少代码重复。

    • 线程安全:使用 volatile 标志位 _isRunning, _firstRate, _lastErrorCode,防止数据竞争。

    3. 硬件适配配置

    支持可配置的硬件查询参数,适配不同硬件。

    csharp

    public class HardwareConfig
    {
    public string QueryInstruction { get; set; } = "DAQREAD";
    public string AdapterBoardQueryInstruction { get; set; } = "DAQREADADAPTERBOARD";
    public int QueryIntervalMs { get; set; } = 100; // 默认 100ms
    }

    public class Hardware : IHardware
    {
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
    private readonly HardwareConfig _config;

    public bool Connected { get; set; }

    public Hardware(HardwareConfig config)
    {
    _config = config ?? throw new ArgumentNullException(nameof(config));
    }

    public async Task<HardwareResult> QueryDataAsync(string instruction, CancellationToken cancellationToken = default)
    {
    await _semaphore.WaitAsync(cancellationToken);
    try
    {
    // 根据配置动态调整指令
    string actualInstruction = instruction switch
    {
    "DAQREAD" => _config.QueryInstruction,
    "DAQREADADAPTERBOARD" => _config.AdapterBoardQueryInstruction,
    _ => instruction
    };
    // 模拟硬件查询
    await Task.Delay(10, cancellationToken); // 替换为实际硬件 API
    return new HardwareResult();
    }
    finally
    {
    _semaphore.Release();
    }
    }

    public async Task<HardwareResult> ReadDataAsync(CancellationToken cancellationToken = default)
    {
    return await QueryDataAsync(_config.QueryInstruction, cancellationToken);
    }
    }

    解释:

    • 配置类:HardwareConfig 允许动态配置查询指令和间隔,适配不同硬件。

    • 动态指令:QueryDataAsync 根据配置选择实际指令,支持硬件差异。

    • 好处:提高代码灵活性,方便适配不同硬件型号或协议。

    4. 性能调优

    • 批量查询(如果硬件支持):

      csharp

      public async Task<HardwareResult[]> BatchQueryAsync(string[] instructions, CancellationToken cancellationToken = default)
      {
      await _semaphore.WaitAsync(cancellationToken);
      try
      {
      // 模拟批量查询
      var results = new HardwareResult[instructions.Length];
      for (int i = 0; i < instructions.Length; i++)
      {
      await Task.Delay(10, cancellationToken); // 替换为实际批量 API
      results[i] = new HardwareResult();
      }
      return results;
      }
      finally
      {
      _semaphore.Release();
      }
      }

      在 StartQueryAsync 中使用:

      csharp

      var results = await m_Hardware.BatchQueryAsync(new[] { "DAQREAD", "DAQREADADAPTERBOARD" }, _cts.Token);
      ProcessBatchResults(results);

      好处:减少硬件访问次数,提高吞吐量。

    • 线程池优化:

      • 使用 Task.Run 运行采集任务,避免手动创建线程:

        csharp

        public async Task StartQueryAsync()
        {
        await Task.Run(async () =>
        {
        // 原 StartQueryAsync 逻辑
        }, _cts.Token);
        }

      好处:利用线程池减少线程创建开销。

    • 状态管理优化:

      • 使用 Interlocked 操作更新标志位:

        csharp

        private int _isRunningFlag = 0;

        private bool IsRunning
        {
        get => Interlocked.CompareExchange(ref _isRunningFlag, 0, 0) == 1;
        set => Interlocked.Exchange(ref _isRunningFlag, value ? 1 : 0);
        }

      好处:确保线程安全的标志位更新。

    5. 日志增强

    在关键点添加详细日志:

    csharp

    private void HandleException(Exception ex, string message)
    {
    ExceptionDoEvnets(ex);
    m_Log.Error($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {Name}: {message}: {ex.Message}, StackTrace: {ex.StackTrace}");
    SystemNotifEvent.HardErrorNotifEvent(this, $"{Name}: {message}", "");
    }

    private async Task RateQueryAsync()
    {
    m_Log.Info($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] Starting RateQuery for {Name}, ReadLooping={ReadLooping}");
    // 原逻辑
    }

    好处:记录时间戳、硬件状态和堆栈信息,便于问题定位。


    优化的详细说明

  • 异步实现:

    • 技术:使用 async/await 和 SemaphoreSlim,支持非阻塞硬件访问。

    • 效果:减少线程阻塞,适合 IO 密集型操作,提高响应性。

    • 适配性:异步方法可无缝对接现代硬件 API(如串口、TCP/IP)。

  • 定时器优化:

    • 技术:PeriodicTimer 提供异步定时,配合 CancellationToken 支持优雅取消。

    • 效果:比 System.Timers.Timer 更轻量,减少资源占用,支持异步流程。

    • 配置:通过 HardwareConfig.QueryIntervalMs 动态调整间隔(默认 100ms)。

  • 硬件适配:

    • 技术:通过 IHardware 接口和 HardwareConfig 类支持不同硬件配置。

    • 效果:提高代码灵活性,适配同步/异步 API 和不同硬件协议。

    • 示例:可配置 QueryInstruction 为特定硬件的指令(如 "READ_SENSOR")。

  • 性能调优:

    • 合并线程:单一异步任务替代两个线程,减少上下文切换。

    • 批量查询:如果硬件支持,合并 DAQREAD 和 DAQREADADAPTERBOARD,减少 IO 开销。

    • 线程池:使用 Task.Run 利用线程池,优化资源分配。

    • 状态管理:Interlocked 确保标志位线程安全。

  • 错误处理和日志:

    • 统一异常处理:通过 HandleException 集中日志和通知。

    • 详细日志:添加时间戳和堆栈信息,方便调试。

    • 效果:提高系统健壮性和可维护性。


  • 注意事项

    • 定时器间隔:HardwareConfig.QueryIntervalMs 默认 100ms,需根据硬件响应时间测试调整(例如 50ms 或 200ms)。

    • 硬件 API:需确认实际硬件 API 是否支持异步或批量查询,若不支持,可回退到同步实现。

    • 取消机制:CancellationToken 确保优雅停止,但需测试取消后硬件状态是否正确重置。

    • 性能测试:在高负载场景下测试采集频率、CPU 使用率和内存占用,确保优化效果。

    • 日志存储:建议将日志写入文件或数据库,避免内存溢出。


    示例使用

    csharp

    var config = new HardwareConfig { QueryIntervalMs = 50 }; // 调整为 50ms
    m_Hardware = new Hardware(config);
    await StartQueryAsync();
    // 停止采集
    await StopQueryAsync();

    如果需要针对特定硬件 API(如串口、Modbus)或进一步性能调优(如连接池、缓存),请提供硬件细节或性能瓶颈描述,我可以提供更定制化的解决方案!

    赞(0)
    未经允许不得转载:171主机测评 » 对 StartQuery 相关代码的进一步细化优化,重点聚焦于异步实现、定时器优化、特定硬件适配和性能调优
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址