【问题标题】:why there is no EndInvoke in Cross thread UI component call ?为什么跨线程 UI 组件调用中没有 EndInvoke?
【发布时间】:2017-01-03 21:47:59
【问题描述】:

我一直在尝试向我的应用程序中的一些 ListBox 添加一些字符串 (简单的 winform )-我是在使用 BeginInboke 时做到的

 myListBox.BeginInvoke(new Action(delegate()
 {
       myListBox.Items.Add( "some string" )); 
 }));

在我再次阅读这 3 行之后 - 我不明白为什么在我在 google 和 MSDN 上查看的任何跨线程 UI 示例中我都没有看到 EndInvoke 的任何调用? 在这种情况下是否有理由不调用 EndInvoke ?

【问题讨论】:

    标签: c#


    【解决方案1】:

    在 .NET 中这是一个不幸的命名选择。 Control.BeginInvoke 和 Dispatcher.BeginInvoke 方法与委托的方法同名,但操作完全不同。主要区别:

    • 委托的 BeginInvoke() 方法始终是类型安全的,它具有与委托声明完全相同的参数。这在 Control/Dispatcher 版本中完全缺失,参数通过 object[] 类型的 params 数组传递。当你得到一个错误的参数时,编译器不会告诉你,它会在运行时爆炸

    • 委托的 Invoke() 方法在同一线程上运行委托目标。 Control/Dispatcher.Invoke() 的情况并非如此,它们将调用编组到 UI 线程

    • 在委托的 BeginInvoke() 目标中引发的异常被捕获,并且不会导致程序失败。在调用 EndInvoke() 时重新抛出。 Control/Dispatcher.BeginInvoke() 根本不是这种情况,它们会在 UI 线程上引发异常。没有合适的方法来捕获异常,这是 Application.UnhandledException 存在的更大原因之一。

    • 需要调用委托的 EndInvoke() 方法,如果不这样做会导致 10 分钟的资源泄漏。 不是 Control/Dispatcher.BeginInvoke() 方法所必需的,而且您在实践中永远不会这样做。

    • 使用 Control/Dispatcher.Invoke() 是有风险的,很容易导致死锁。当 UI 线程尚未准备好调用目标并且执行一些不明智的操作(例如等待线程完成)时触发。对于委托来说不是问题,至少因为它的 Invoke() 方法不使用线程。

    • 在 UI 线程上调用 Control/Dispatcher.BeginInvoke() 是受支持的方案。正如预期的那样,目标仍然在 UI 线程上运行。但后来,在 UI 线程再次空闲并重新进入调度程序循环之后。这实际上是一个非常有用的功能,它有助于解决棘手的重入问题。尤其是在 UI 控件的事件处理程序中,当您运行具有太多副作用的代码时会出现异常。

    一个包含大量实现细节的大列表。 TLDR 版本当然是:“它们没有任何共同点,不调用 EndInvoke 很好,完全正常”。

    【讨论】:

    • 很好的答案! Control.…InvokeTDelegate.…Invoke 之间的比较列表应该在 MSDN 上。 -- 我只建议您在旁注中解释 WPF/Silverlight 的 Dispatcher,因为它在 Windows 窗体中不存在(这是这个问题的上下文)。
    • 谢谢你 - 我一直在通过 Control.cs 想知道如何修复空队列:threadCallBackList。现在我可以高枕无忧并删除导致问题的 EndInvoke! :)
    【解决方案2】:

    Control.BeginInvoke 似乎没有完全遵循通常的BeginX/EndX 模式,也就是Asynchronous Programming Model (APM)。通常,您必须为每个BeginX 调用EndX,但对于Control.BeginInvoke,这不是严格要求:

    “您可以调用EndInvoke从委托中检索返回值,如果需要,但这是不需要 >. EndInvoke 将阻塞,直到可以检索到返回值。”

    ——来自MSDN reference page for Control.BeginInvoke 的备注部分(我强调)

    在实践中,几乎没有必要。这是因为通常调用该方法是为了让一些代码在更新 UI 的 UI 线程上执行。更新 UI 通常不会产生任何返回值,因此您不会想调用 EndInvoke

    【讨论】:

    • 删除了我的帖子,因为我已经编辑了几次,它最终看起来很像你的:)
    • 据我所知 - 如果您不调用 EndInvoke 来关闭从 BeginInvoke 返回的 IAsyncResult - 我会出现内存泄漏 - 如果我错了请纠正我
    • @Yanshof:在这种特定情况下(Windows 窗体的Control.BeginInvoke),在不调用Control.EndInvoke 时没有任何内存泄漏迹象。毕竟,这是一种普遍的做法,也是一种官方记录的模式。如果它导致内存泄漏,那么在 Windows 窗体存在多年的时间里,一定有人会注意到。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-21
    • 1970-01-01
    • 2016-03-20
    • 2022-01-10
    • 2021-03-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多