如何在 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 互操作 —— 生命周期和内存泄漏

定时器停止更新页面的 Blazor Server 问题

发布请求和 QNetworkAccessManager 的内存泄漏

如何在 Blazor 项目中使用 C# 绑定制作 HTML 文本多行?

。net应用程序在特定计算机中的内存泄漏