You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor Server应用Scoped服务PLC数据无法实时更新至Razor页面问题

Blazor Server Scoped服务实时数据更新失败问题排查与修复

核心问题分析

  1. 事件订阅时机滞后
    在call_read_data_service方法中,你先等待read_data_cyclic循环执行完成(总时长10秒),之后才订阅Item_Changed事件。这意味着循环过程中触发的所有事件都无法被组件接收,自然看不到实时更新。

  2. UI线程被阻塞
    await Task.Run(...)之后调用的Thread.Sleep(5000)会直接阻塞UI线程,导致Blazor无法及时响应数据更新请求,甚至引发页面卡顿。

  3. 事件触发参数错误
    服务中Pass_item方法的事件触发逻辑存在参数错误:

Item_Changed?.Invoke(Item_Changed, new_item);

第一个参数应为事件发送者(服务实例本身this),而非事件委托对象,错误参数可能导致事件处理逻辑异常。

  1. 事件订阅未规范化管理
    未在组件初始化阶段订阅事件,也未在组件销毁时取消订阅,既容易错过早期事件,也可能引发内存泄漏。

修复方案

1. 修正Scoped服务的事件触发逻辑

修改Pass_item方法,将事件发送者参数改为服务实例本身:

public string Pass_item(string new_item)
{            
    Item_Changed?.Invoke(this, new_item);
    return new_item;
}

2. 规范化组件的事件订阅时机

在组件初始化时订阅事件,销毁时取消订阅,确保全程能接收事件:

// 让组件实现IDisposable接口
public partial class YourComponent : IDisposable
{
    protected override void OnInitialized()
    {
        Data_reader_service.Item_Changed += Item_Changed;
    }

    public void Dispose()
    {
        Data_reader_service.Item_Changed -= Item_Changed;
    }
}

3. 修复按钮点击方法的逻辑

移除阻塞UI的Thread.Sleep,直接启动后台循环任务即可:

public async Task call_read_data_service()
{ 
    // 使用_=丢弃任务,避免阻塞UI线程
    _ = Task.Run(() => Data_reader_service.read_data_cyclic());
}

若需确保PLC连接成功,建议在服务内部添加连接状态校验,而非通过睡眠阻塞UI。

4. 优化服务的循环逻辑(可选)

将同步睡眠改为异步等待,避免占用线程池线程:

// 将方法改为异步
public async Task read_data_cyclic()
{
   try
   {         
       for(int y=0; y<10; y++)
       {
           // PLC数据读取逻辑...
           using (StreamWriter sw2 = File.AppendText(log_path))
           {
               sw2.WriteLine($"val-{y}:{item_value} {DateTime.Now}");
           }                                           
           Pass_item(item_value);            
           // 异步等待,不阻塞线程
           await Task.Delay(1000);
       }
   }
   catch
   {
       // 错误处理逻辑
   }
}

对应的组件调用简化为:

public async Task call_read_data_service()
{ 
    _ = Data_reader_service.read_data_cyclic();
}

5. 简化事件处理方法

事件处理方法可简化为如下形式(保持线程切换逻辑即可):

private void Item_Changed(object sender, string new_item)
{
    InvokeAsync(() =>
    {
        item_value = new_item;
        StateHasChanged();
    });
}

修复后流程说明

  1. 组件初始化时立即订阅服务事件,确保不会错过任何一次数据更新。
  2. 点击按钮后,后台线程启动PLC数据循环读取任务,不阻塞UI。
  3. 服务每次读取数据后正确触发事件,组件通过InvokeAsync切换到UI线程更新数据并通知Blazor重新渲染页面。
  4. 组件销毁时取消事件订阅,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Mdarende

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 09:00:55