Blazor Server应用定时器定时更新页面运行一段时间后停止问题求助
故障原因分析
- 定时器被GC意外回收:初始版本的
System.Threading.Timer是OnInitialized方法内的局部变量,没有实例级别的根引用,运行一段时间后GC触发回收时会销毁定时器,直接导致回调停止执行。 - 异步阻塞引发死锁:两个版本的代码都用
.Result同步阻塞等待异步方法返回,Blazor Server每个Circuit存在专属同步上下文,阻塞等待会导致同步上下文被占满,异步方法完成调度时无法获取上下文资源,形成死锁,后续定时器回调将无法被正常执行。 - 数据库同步查询阻塞:数据库查询使用同步方法
Single而非异步实现,数据库IO延迟时会占用线程池资源,高频率调用下容易导致线程池耗尽,定时器回调无法被调度。
解决方案
- 将定时器声明为组件的实例字段,避免作为局部变量被GC回收
- 全链路使用
await实现异步调用,移除所有.Result、.Wait()类阻塞代码,规避死锁风险 - 数据库操作替换为异步实现,减少线程池资源占用
- 正确实现
IAsyncDisposable接口,组件销毁时主动释放定时器资源
修正后的完整代码
@page "/Refresh2" @inject IDbContextFactory<SQLbeginnerContext> DbFactory @using TestBlazorServer.Models @using Microsoft.EntityFrameworkCore @using System.Timers @implements IAsyncDisposable <h3>PageRefresh2</h3> <h1>@Time</h1> <h1>@currentCount</h1> <h1>TestValue:@databaseValue</h1> @code { private int currentCount = 0; // 声明为实例字段,防止GC回收 private readonly Timer _timer2 = new(1000); private string Time { get; set; } private int? databaseValue { get; set; } protected override void OnInitialized() { _timer2.Elapsed += async (sender, eventArgs) => await OnTimerCallback(); _timer2.AutoReset = true; _timer2.Start(); } private async Task OnTimerCallback() { await InvokeAsync(async () => { currentCount++; Time = DateTime.Now.ToString(); // 用await替代.Result,避免同步阻塞 databaseValue = await TimerProcessGetValue(); StateHasChanged(); }); } // 异步释放定时器资源 public async ValueTask DisposeAsync() { await _timer2.DisposeAsync(); } public async Task<int?> TimerProcessGetValue() { int? timerProcessValue; await using (var dbContext = DbFactory.CreateDbContext()) { // 数据库查询替换为异步实现,减少线程阻塞 timerProcessValue = (await dbContext.TestTable.SingleAsync(x => x.Id == 1)).TestValue; } return timerProcessValue; } }
额外说明
如果修改后仍然出现长时间运行后停止的情况,大概率是Blazor Server的Circuit被服务器判定为空闲后回收,属于服务端正常的资源回收机制,可在前端添加简单的定时心跳逻辑,避免Circuit被判定为 idle 状态。
内容的提问来源于stack exchange,提问作者BTOM
相关产品推荐
相关产品推荐

