【问题标题】:Why use Invoke on Controls in .net? [duplicate]为什么在 .net 中对控件使用 Invoke? [复制]
【发布时间】:2012-10-06 13:48:15
【问题描述】:

可能重复:
Why .NET does not allow cross-thread operations?
Why is only the UI thread allowed to modify the UI?

据我了解,.net 引发非法跨线程调用异常的原因是 GUI 可能会显示不确定的行为。

但不是所有其他对象都是如此吗?如果两个线程在同一个对象上工作,根据代码,该对象可能处于不确定的情况。那么为什么控制元素存在这个例外呢?或者为什么这个异常是控制元素独有的。

那么使用调用有什么帮助呢?它仍然是不确定的。

【问题讨论】:

  • 调用——这不仅仅是一个好主意;这是法律。
  • 这不是真正的重复,这两个问题都是在问为什么不允许跨线程通信。这个问题是在询问 Invoke 的使用。

标签: c# .net invoke


【解决方案1】:

Invoke 用于确保您在正确的线程上调用 UI。但这是每个 .net 对象都需要的。 UI 可以在单个线程上工作(WPF 和 Winforms 也是如此)。两个不同的线程可以访问同一个对象,只要它不同时。如果发生这种情况,它会产生竞争情况并可能以死锁结束

【讨论】:

  • 但这确实取决于您的代码。你可以有一个带有对象的竞赛案例。另一方面,您可以让多个线程更改 UI,但通过创建控制锁等仍然具有确定性行为。例如,如果单击了 button1,则线程 2 可以添加 button2。如果 button2 被点击,那么线程 3 可能会改变 GUI 等等。
  • 没有不同的线程改变 UI 会导致异常
【解决方案2】:

大多数类型实际上不是线程安全的,但可以在多个线程中使用,只要一次只有 一个 线程使用它们。

UI 控件并不完全相同:它们具有线程亲和性 - 它们只能在 UI 线程上安全使用。

部分原因是 UI 线程可能想要随时重绘它们,因此它们必须随时可用。将其与不希望在等待锁定时延迟 UI 线程相结合,将它与希望避免甚至无争议的锁定相结合,如果你可以帮助它(并且 UI 线程将不得不采取如果需要为每次访问都执行此操作,则取出 很多 锁),您将获得一个简化的模型,其中更容易说所有访问 必须 在 UI 线程上.我确信可能设计一个没有线程亲和性的 UI 框架,但是让它以可预测的方式执行和表现将是棘手的。

【讨论】:

    【解决方案3】:

    Invoke-ing 控制其他线程是必需的,因为cross-thread calls to Controls are not allowed。要更完整地讨论为什么存在此限制,您应该阅读该链接 - 我不会在这里回答,它只是 (但请确保此限制存在于很好的理由)。

    调用Invoke 对我们有帮助,因为它允许后台线程在 UI 线程上“做事”——它之所以有效,是因为它不直接调用该方法,而是发送一个 Windows message,上面写着“当你有机会”。 UI 线程正在运行message pump,它持续处理发送到该线程的所有消息——通常这些消息类似于"the user clicked on this button",在这种情况下,Windows 窗体通过在相关控件上引发Click 事件来处理此消息。在这种情况下,Windows 窗体通过运行提供的委托来处理消息。

    结果是只有 1 个线程在任一时间点(UI 线程)修改/使用 UI 控件。

    请注意,Invoke 不保证代表的运行顺序。如果以正确的顺序执行来自两个不同线程(甚至同一个线程)的两个代表Invoked 很重要,那么这就是另一个问题了。


    旁白:我们谈论“UI 线程”是因为大多数应用程序都有一个线程在其上创建所有控件,但实际上可以创建不同的控件线程 - 它是控件所在的线程在哪个进程上创建消息。显然,为了正确处理这些消息,必须在该线程上运行一个消息泵。

    【讨论】:

    • 再一次,这不会让它变得可预测。假设您有 UI 线程和另一个线程,并且都尝试更改 UI。其他线程消息可能会在 UI 线程更改之前或之后出现,从而导致不可预测性。
    • @user1316459 这不是Invoke 试图解决的问题 - Invoke 存在是因为在 Windows 窗体中明确禁止跨线程 UI 调用(请参阅我的回答中的第一个链接以进行讨论为什么)。如果 UI 线程操作在另一个线程之前/之后很重要,那么这是一个完全不同的问题 - Invoke 不保证消息的处理顺序。
    • 好吧,首先,它并不是完全不允许的。您可以将 Control.checkforillegalcrossthreadcalls 设置为 false。但如果我不将其设置为 false,正如许多人所鼓励的那样,我将不得不使用调用。我的观点是,它并没有使任何事情变得更好。我的 UI 将具有相同的可预测性状态。
    • @user1316459 “无跨线程 UI 调用”规则保护控件的内部状态比使多线程可预测更多。例如,如果ListView 在将项目添加到其Items 集合和将项目添加到底层本机控件之间被中断,则可能会发生各种坏事。编写一个没有这些限制的 UI 框架并非不可能,但它非常很困难。
    猜你喜欢
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-12
    • 1970-01-01
    相关资源
    最近更新 更多