如何在 Blazor 应用程序中制作我的计时器泄漏证明?
Posted
技术标签:
【中文标题】如何在 Blazor 应用程序中制作我的计时器泄漏证明?【英文标题】:How can I make my Timer leaking proof in Blazor App? 【发布时间】:2021-03-11 02:01:48 【问题描述】:我已经在 *** C# counter to count up to a target number 中发布了一个问题的答案。答案如下:
您可以创建一个可以在很多场合为您服务的计时器服务:
创建服务类:
public class BlazorTimer
private Timer _timer;
internal void SetTimer(double interval)
_timer = new Timer(interval);
_timer.Elapsed += NotifyTimerElapsed;
_timer.Enabled = true;
_timer.Start();
private void NotifyTimerElapsed(object sender, ElapsedEventArgs e)
OnElapsed?.Invoke();
public event Action OnElapsed;
在 Program.Main 方法中将服务作为瞬态添加到 DI 容器中:
builder.Services.AddTransient(config =>
var blazorTimer = new BlazorTimer();
blazorTimer.SetTimer(1000);
return blazorTimer;
);
用法
@page "/"
@implements IDisposable
@inject BlazorTimer Timer
@count.ToString()
@code
private int count = 0;
protected override void OnInitialized()
Timer.OnElapsed += NotifyTimerElapsed;
base.OnInitialized();
private void NotifyTimerElapsed()
// Note: WebAssembly Apps are currently supporting a single thread, which
// is why you don't have to call
// the StateHasChanged method from within the InvokeAsync method. But it
// is a good practice to do so in consideration of future changes, such as
// ability to run WebAssembly Apps in more than one thread.
InvokeAsync(() => count++; StateHasChanged(); );
public void Dispose()
Timer.OnElapsed -= NotifyTimerElapsed;
但是,有人告诉我
BlazorTimer 正在泄漏 _timer。定时器是 IDisposable
取消订阅 Blazor 组件中实现的 Dispose 方法中的事件处理程序是否会导致 BlazorTimer 泄漏 _timer。实际上我并不完全理解“BlazorTimer 正在泄漏 _timer。Timer is IDisposable”,所以让我问一下,我怎样才能防止计时器泄漏,但在实现的 Dispose 方法中使用代码取消订阅事件处理程序Blazor 组件?除了跳过事件处理程序的取消订阅之外,还有什么方法可以防止这种泄漏。
【问题讨论】:
【参考方案1】:好的,感谢mkArtakMSFT 和Peter Morris,我已经解决了这个问题。
一般的经验法则是,每次封装一个 一次性类型的新类型作为会员,你应该让你的新 也输入一次性的。在您的特定情况下, BlazorTimer 类是 不是一次性的 - 因此底层的 _timer 实例,当 初始化,永远不会被处理 - 留下一些内存。
此外,每次调用 SetTimer 方法时,都会创建一个新的 Timer 正在创建实例,而旧的实例被留下(在 空气),再次泄漏内存。考虑处置现有的 例如,如果这是你的意图。或者,更好的是,重用现有的 例如,如果您的业务规则可以接受 Source
注意:“BlazorTimer 类不是一次性的”,因为我使用的是瞬态依赖项。将其作为一次性使用会产生不利影响...参见说明blazor-university
为了解决内存泄漏问题,在我当前的代码 sn-p 中,我应该简单地从 NotifyTimerElapsed 方法对 Timer 对象调用 Dispose 方法(总是这样做,这次忘了)
【讨论】:
【参考方案2】:BlazorTimer 应实现 IDisposable。 BlazorTimer Dispose 方法应停止计时器、取消订阅 Elapsed 事件并释放计时器。
问题的根源在于您的 BlazorTimer 设置为瞬态服务。因此,对于每个新请求,您都会获得带有新 .Net 计时器的新 BlazorTimer 对象,这些计时器永远不会被正确处理
【讨论】:
"BlazorTimer 应该实现 IDisposable" 请解释原因..."只有当您的类型直接使用非托管资源时,您才应该实现 IDisposable。" (docs.microsoft.com/en-us/dotnet/api/…)。我是否直接使用“非托管资源”? “因此,对于每个新请求,您都会获得带有新 .Net 计时器的新 BlazorTimer 对象”我知道。 “永远不会正确处理”这是问题所在:我如何知道或验证新的 BlazorTimer 对象“永远不会正确处理” 你说:“BlazorTimer 应该实现 IDisposable”。但是我读到“只有在类不实现 IDisposable 时才将类注册为瞬态依赖项,否则,您的应用程序将泄漏内存。”在blazor-university.com/dependency-injection/… 我想这意味着我的应用程序没有泄漏,对吧?采纳你的建议只会损害我的申请,不会改善我的申请。 如果这实际上是框架处理瞬态的方式,那么这是微软应该解决的一个严重问题。这实际上意味着您永远不能将 IDisposable 用作临时服务,这既不可接受,也不直观。 Microsoft 应该以这样一种方式处理范围,即当您将服务注入的页面被 Disposed 时自动调用 Dispose,感谢您让我意识到框架中的这一限制。以上是关于如何在 Blazor 应用程序中制作我的计时器泄漏证明?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我的 Blazor 服务器应用程序的计时器没有刷新其组件
Blazor University (42)JavaScript 互操作 —— 生命周期和内存泄漏
发布请求和 QNetworkAccessManager 的内存泄漏