Blazor Server应用定时器无法周期性执行PLC数据读取问题
问题分析与修复方案
你的Blazor Server定时器不工作、计数器始终为0的问题,主要由以下几个代码错误导致:
核心问题点
- 变量名不匹配:定时器事件里更新的是
counter_CNC_status,但页面绑定的是counter_timer,两者不一致导致页面显示永远为0。 - 非UI线程更新状态:
System.Timers.Timer的Elapsed事件在后台线程触发,直接调用StateHasChanged()无法触发Blazor组件的UI渲染,因为Blazor要求状态更新必须在UI同步上下文执行。 - 异步方法规范问题:
Read_CNC_Status使用async void,这在Blazor中会导致异常无法被捕获,应该使用async Task。 - 重复绑定事件风险:多次点击按钮会重复添加
Elapsed事件处理程序,导致定时器触发逻辑混乱。 - 未释放资源:组件销毁时未释放定时器,会引发内存泄漏。
修复后的完整代码
Razor页面代码
@inject PLCService PLCService @implements IDisposable <button @onclick="Read_CNC_Status">Read PLC Data</button> <span>@counter_timer</span>
代码后置文件(Read_PLC_Data.razor.cs)
using System.Timers; using Microsoft.AspNetCore.Components; public partial class Read_PLC_Data : IDisposable { public int counter_timer = 0; private System.Timers.Timer? _timer; [Inject] public PLCService PLCService { get; set; } = default!; public async Task Read_CNC_Status() { PLCService.Connect_PLC(); InitializeTimer(); } private void InitializeTimer() { // 先销毁旧定时器,避免重复绑定事件 _timer?.Dispose(); _timer = new System.Timers.Timer(1000); _timer.Elapsed += OnTimedEvent; _timer.AutoReset = true; // 显式设置自动重置,确保每秒触发一次 _timer.Enabled = true; counter_timer = 0; } private async void OnTimedEvent(object source, ElapsedEventArgs e) { // 通过InvokeAsync将逻辑切换到UI线程执行 await InvokeAsync(() => { PLCService.Read_PLC_Data(); counter_timer += 1; // 修正变量名,与页面绑定一致 if(counter_timer >= 30) { counter_timer = 0; _timer?.Enabled = false; } StateHasChanged(); }); } // 实现IDisposable,组件销毁时释放定时器资源 public void Dispose() { _timer?.Dispose(); } }
关键修复说明
- 变量名统一:将事件中的计数器变量改为
counter_timer,与页面绑定的变量保持一致,确保页面能正确显示计数。 - UI线程同步:使用
InvokeAsync包裹状态更新逻辑,强制在UI线程执行,保证StateHasChanged()能触发组件重新渲染。 - 异步方法规范:把
Read_CNC_Status改为async Task,符合Blazor的异步处理要求,避免异常丢失。 - 定时器资源管理:每次初始化定时器前先销毁旧实例,防止重复绑定事件;实现
IDisposable接口,组件销毁时释放定时器,避免内存泄漏。 - 显式配置定时器:设置
AutoReset = true,明确定时器每秒自动触发一次,逻辑更清晰。
内容的提问来源于stack exchange,提问作者Mdarende
相关产品推荐
相关产品推荐

