【问题标题】:Timer sometimes not triggering定时器有时不触发
【发布时间】:2023-04-03 22:17:01
【问题描述】:

---更新---

我现在可以说,每次计时器无法触发时,我都会在调试输出中收到以下错误消息。当我收到错误时,计时器 100% 的时间都失败了。

Failed to load resource: net::ERR_HTTP2_PROTOCOL_ERROR [https://localhost:44371/_framework/blazor.server.js] Exception thrown: 'Microsoft.AspNetCore.Connections.ConnectionResetException' in Microsoft.AspNetCore.Server.IIS.dll

但是,live site(在 Windows 服务器上)有时也无法触发 Timer,所以我认为这不仅仅是 VS 调试问题。

---/更新---

我的门厅页面上有一个持续 3 秒的动画 svg。我想在 3 秒后显示其他控件:登录按钮、“参观”等等,所以我添加了一个在 OnAfterRender 中启动的 Timer(也尝试过 OnAfterRenderAsync)。

问题是大约有 1/3 的时间(随机地很少为 1/20 或更多),计时器的处理程序没有触发,并且控件没有显示。我假设这是某种线程竞争条件,但我真的不明白可以做些什么。

我试过了:

  • 使用async 生命周期事件和处理程序
  • await ()=> 表达式等各种组合

(注意——我知道我可以使用动画 CSS 来做到这一点,但我现在限制测试计时器的使用)

下面是一些简化的代码:

<MyCoolAnimation / >
@if (ShowControls)
{
    // various controls
}

@code {
    bool ShowControls;
    protected override void OnAfterRender(bool firstRender)
    {
        if (!firstRender)
        {
            Timer = new Timer(3000);
            Timer.Elapsed += CountDownTimer;
            Timer.Start();
        }    
    }
    private async void CountDownTimer(Object source, System.Timers.ElapsedEventArgs e)
    {
        Timer.Stop();
        Timer.Dispose();
        ShowControls = true;

        await InvokeAsync(StateHasChanged);
    }
}

【问题讨论】:

  • 请阅读How to Ask,然后阅读minimal reproducible example。然后,请向我们提供您尝试过的各种操作的完整代码以及我们复制您的问题的说明。现在您的问题似乎没有包含足够的信息让我们给您一个好的答案。一个好的问题可以在几分钟内得到答案。
  • 该指令将您想要的任何内容放入 @if 块中,然后重新加载大约 20 次,也许是 30 次。我无法给出比这更小的可重现示例。我也无法列出我尝试使用 async Tasks 或生命周期覆盖来找到任何不同的许多方法,因为我已经尝试了很多方法,但结果没有明显的变化。
  • “我不能给出一个比这更小的可重现的例子。” - 你当然可以。如果我只是创建一个 Blazor 应用程序并粘贴到您的代码中,它甚至不会为我运行。请创建一个示例应用程序来演示您的问题,并向我们提供运行该应用程序的说明。这应该不难做到。
  • 我使用来自服务的类似心跳的事件。整个应用程序的 1 个计时器。委托传递计数或当前日期时间。无论哪种方式,您都可以使用它来计算持续时间。
  • Blazor 服务器还是 WebAssembly? Timer 如何/在哪里声明?您离minimal reproducible example 很近,但只是不在那里。

标签: c# timer blazor


【解决方案1】:

在这种情况下,我会避免整个计时器问题。

您可以在没有 Timer、Dispose 模式或 InvokeAsync 的情况下执行此操作。保持简单:

<MyCoolAnimation / >
@if (ShowControls)
{
    // various controls
}

@code {
  bool ShowControls;

  protected override async Task OnInitializedAsync()
  {
    ShowControls  = false;
    await Task.Delay(3_000);
    ShowControls  = true;
  }
}

我对调用 Timer.Dispose() inside 已过去的处理程序有疑问。但是找不到关于它的权威声明。

【讨论】:

  • 是的,我也不确定,但我在没有DisposeStop 的情况下尝试过,它并没有改变任何东西。我不知道如何获取堆栈跟踪,因为我无法捕捉到 net::ERR_HTTP2_PROTOCOL_ERROR 错误,或 catch try 块中随附的 'Microsoft.AspNetCore.Connections.ConnectionResetException' in Microsoft.AspNetCore.Server.IIS.dll。但有一件事——在查看调试输出时,该错误会在页面加载时立即出现,而不是在 3 秒后出现。所以我看到了错误,说“好的,它不会弹出”,然后就没有了。
  • 放弃吧,Bennyboy1973。 ConnectionResetException 都是 IIS 的,和定时器无关
  • @enet 是的,我现在已经完成了。不过,感谢您的帮助,我很感激。
【解决方案2】:

if (!firstRender) 很奇怪——你会有很多计时器在运行。

这有点提供了答案,Timer = new Timer(3000);Timer.Dispose(); 部分具有竞争条件(至少在 Blazor 服务器上)。

  • 确保您的页面上只有 1 个 Timer 实例
  • 在 OnInitialized() 中创建并启动它
  • 使用@implements IDisposable进行清理

Here 是一些示例代码。

【讨论】:

  • 糟糕——我实际上接受了这个作为答案,但是在更改为if(firstRender) 之后,它又失败了。这是非常罕见的——比如加载 1/20 左右的页面。
  • 问题不在于您当时认为的问题所在。创建一个新的临时项目并尝试重现它。
  • 我现在有一些额外的信息。我会把它添加到 OP 中。
  • 谷歌搜索后,我发现这个错误似乎是最近的 Windows 更新问题。我担心网站中的其他事件也可能被随机破坏。
  • 更新——将初始化移动到 OnItialized,只有在它为 null 时才创建新的 Timer 等,没有任何效果。如有必要,我可以使用页面的当前状态创建答案。
【解决方案3】:

(v3)

最后更新:

我已经结束了。谢谢你们的帮助,伙计们!现在,Henk 的以下建议有效。我进行了 Windows 更新,我认为这是问题所在 - 但我仍然无法让 Timer 版本正常工作。我谨慎乐观地认为问题在于生命周期,而不是一般的计时器。

protected override async Task  OnAfterRenderAsync(bool firstRender)
{
    if (!ShowControls)
    {
        await Task.Delay(3000);
        ShowControls = true;
        StateHasChanged();
    }
}

在我看来,在 Blazor 生命周期中实例化 Timer 对象甚至可能过于挑剔而无法使其可靠地工作。

我唯一的希望是我使用 Timer 做的其他事情(比如通过服务弹出排队的消息)也不会在 5% 的时间内中断。

【讨论】:

  • 为什么要放在 OnAfterRender 中?除非你有一些 JS,否则这不是“正常的地方”。
  • 没有特别的原因。我只是在尝试和限制测试,当我发现效果不再失败时,我正在尝试这个事件。我打算在服务中使用计时器制作一个玩家对玩家的问答游戏,我想更好地了解这个领域。
猜你喜欢
  • 2018-03-26
  • 2014-07-17
  • 1970-01-01
  • 1970-01-01
  • 2013-03-27
  • 1970-01-01
  • 1970-01-01
  • 2010-11-18
  • 1970-01-01
相关资源
最近更新 更多