Control.Invoke() 与其委托的调用之间的延迟是多长时间?

Posted

技术标签:

【中文标题】Control.Invoke() 与其委托的调用之间的延迟是多长时间?【英文标题】:How long is the delay between Control.Invoke() and the calling of its Delegate? 【发布时间】:2010-02-20 21:47:54 【问题描述】:

我有一个代码引擎,它通过使用 waveOutOpen 和 waveOutWrite API 方法连续播放较小的块来播放长 WAV 文件。为了在文件播放时更新我的​​ UI,在每个缓冲区完成播放时从回调函数中调用一个单独的线程(因为您希望在回调函数中尽可能少地执行此操作),该线程在我的表单中调用一个方法。

表单包含一个类级别EventHandler,它处理一个方法,在该方法中我使用新信息更新 UI 元素。在从 waveOutWrite 回调函数调用的表单方法中,我使用 Invoke 方法如下:

if (_updatedisplay == null)

    // UpdateDisplay contains code to set control properties on the form
    _updatedisplay = new EventHandler(UpdateDisplay);

Invoke(_updatedisplay);

一切正常,但 UI 元素的更新有时会出现明显的滞后或延迟。这很容易看出,因为我使用 UpdateDisplay 方法来驱动动画,因此延迟显示为“打嗝”,精灵在跳到预期位置之前似乎冻结了一瞬间。

这样的跨线程通信是否可能有时会出现很长的延迟(可能是 10-15 毫秒)?如果是这样,有什么更好的方法来处理这样的事情?

更新:顺便说一句,我绝对确定Invoke 是这里的罪魁祸首。另一种可能性是一段音频播放完毕和回调函数实际被调用之间的延迟。

更新 2:根据itowlson 的建议,我使用System.Diagnostics.Stopwatch 来对Invoke 和方法调用之间的延迟进行基准测试。在 1156 次测量中,我在 0 毫秒时得到 1146 次,在 1 毫秒时得到 8 次,在 2 毫秒时得到 2 次。我认为可以肯定地说Invoke 不是我的罪魁祸首。

【问题讨论】:

【参考方案1】:

是的,可以有任意长的延迟。 Invoke 通过向目标控件发送 Windows 消息来工作,因此它只会在目标线程泵送消息时得到处理。如果线程已经在处理一条消息,并且该处理需要时间,那么在线程泵出其下一条消息并因此处理 Invoke 之前可能会有明显的延迟。

更好的方法可能是调用 BeginInvoke。这并不能避免 UI 线程处理消息的潜在延迟,但它可以避免您的调用线程在等待 UI 线程泵送消息时被阻塞。但是,这可能对您的场景没有帮助,因为它听起来像是 UI 线程的忙碌导致动画中出现故障。

更新以响应您的更新:请注意,我在这里要说的是可能会有任意长的延迟,而不是 em> 是明显的延迟,或者这绝对是您延迟的原因。除非在 UI 线程上发生了真正密集的事情,否则 10 到 15 毫秒对于应用程序来说似乎确实是一个异常长的时间,因此考虑其他原因当然是明智的!

【讨论】:

我刚刚尝试使用 BeginInvoke,但它仍然打嗝。另外,不久前我在尝试使用高分辨率计时器的回调运行动画时遇到了同样的问题,并且遇到了同样的故障。我想知道这是否是 .Net UI 的基本要素? 不,至少就 Windows 而言,它是 任何 UI 的基础。 Windows 窗体中的消息体系结构并不是什么新鲜事物,即使是 C++ 应用程序在 Windows 中的工作方式也是一样的。如果您尝试更新进度条或不应该跨线程调用的东西。而是对进度变量使用联锁写入,UI 线程会在特定时间间隔读取该变量。 听起来maybe 动画组件在泵送消息方面并不是一个好公民,但我很犹豫推测什么是在这里进行,尤其是因为您不确定 Invoke 是问题所在。尝试捕获调用 BeginInvoke 和 UpdateDisplay 开始执行的时间:这可能有助于确定跨线程调用是否确实是问题所在。 (您需要使用高分辨率计时。) 嗯,不是Invoke,每帧动画的渲染时间不到1毫秒,所以不是这样。我剩下的两个罪魁祸首是 waveOutWrite 回调本身的延迟,或者是我的帧速率与屏幕刷新率同步的问题。不幸的是,我对任何一个都无能为力。

以上是关于Control.Invoke() 与其委托的调用之间的延迟是多长时间?的主要内容,如果未能解决你的问题,请参考以下文章

Invoke调用中的匿名方法

Invoke()/BeginInvoke()区别

C#中线程对控件的访问

类型安全控件。调用C#

带有 out 参数的 control.invoke

"在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke"