【问题标题】:Isn't blindly using InvokeRequired just bad practice?盲目使用 InvokeRequired 不是不好的做法吗?
【发布时间】:2013-07-02 09:14:17
【问题描述】:

我是一个新手程序员,所以我在这里可能完全搞错了,但是这个问题让我感到不安。

这实际上是this问题的后续。

接受的答案是,您必须调用 InvokeRequired 以避免一些开销,因为您有可能已经在 UI 线程上进行操作。

理论上,我同意它可以节省一些时间。经过一些测试,我发现使用 Invoke 所花费的时间大约是正常调用操作的两倍(测试比如设置标签的文本 n 次,或者在 RichTextBox 中放置一个非常非常大的字符串)。

但是!然后是练习。

MSDN 文档说:

此属性可用于确定您是否必须调用调用方法,如果您不知道哪个线程拥有控件,这将很有用。

在大多数情况下,您确实知道何时尝试从另一个线程访问控件。实际上,我能想到的唯一情况是,当从可以由线程 X 以及所有者线程调用的方法访问控件时。这对我来说是一个非常不可能的情况。

即使您真的不知道哪个线程试图操纵控件,也有一个事实是 UI 线程不必那么频繁地更新。 25-30 fps 之间的任何内容都适合您的 GUI。在 UI 控件中进行的大多数更改只需几毫秒即可执行。

因此,如果我理解正确,您必须检查是否需要调用的唯一情况是当您不知道哪个线程正在访问控件以及 GUI 更新需要超过 40 毫秒才能完成时。

然后是我在http://programmers.stackexchange.com 上提出的this 问题的答案。这表明您不应该在不需要时忙于过早的优化。特别是如果它牺牲了代码的可读性。

所以这给我带来了我的问题:当您知道不同的线程访问控件时,您不应该只使用调用吗,并且当您知道您的 UI 线程可以访问那段代码时使用 并且您发现它应该运行得更快,您应该检查是否需要调用?

PS:在校对我的问题后,听起来我真的在咆哮。但实际上我只是好奇为什么 InvokeRequired 似乎被许多比我更有经验的程序员过度使用。

【问题讨论】:

  • 你的帖子做得很好。
  • 我大体上同意 - 如果您希望在非 UI 线程上进行调用,那么使用 InvokeRequired 将代码弄乱是没有意义的。
  • 我的 2 美分:InvokeRequired 被如此频繁地使用是因为这些程序员做出了需要它的伤害体验,即使他们一开始就确定他们不需要它。他们学会了避免不得不考虑在特定情况下是否需要它,他们总是把它放进去。这使他们更有效率(花更少的时间试图弄清楚是否可能需要它,更少的时间花在稍后发现错误时修复代码)。这很好。这也使他们更少考虑自己在做什么。这很糟糕。
  • 如您所见,我们对此表示赞赏。太多帖子低于标准。很高兴看到额外的努力。
  • 作为一名对他人文档记录不佳的代码进行大量维护的开发人员,我讨厌多余的检查。 IME,我开发的从辅助线程和主 UI 线程调用显式 UI 方法的应用程序的数量大约为零。

标签: c# multithreading invoke invokerequired


【解决方案1】:

你在这里断章取意。第一个问题you linked 链接了另一个问题,该问题专门关于编写线程安全 方法来访问UI 控件。

如果您不需要对 UI 控件的线程安全访问,因为您知道不会从另一个线程更新它,那么当然,您不应该使用这种技术。无需使用 InvokeRequiredInvoke 即可轻松更新您的 UI 控件。

另一方面,如果调用总是源自 UI 线程以外的线程,则只需使用 Invoke 而无需先检查 InvokeRequired

这导致了三个简单的规则:

  1. 如果您仅从 UI 线程更新控件,请不要使用 InvokeRequiredInvoke
  2. 如果仅从 UI 线程以外的线程更新控件,请仅使用 Invoke
  3. 如果您同时从 UI 线程和其他线程更新控件,请结合使用 InvokeInvokeRequired

【讨论】:

  • 我同意,但不应该是这样:“3. 如果您从 UI 线程和其他线程更新控件,并且一切都按预期工作,请使用 Invoke。4.如果您更新控件来自 UI 线程和其他线程,并且仅使用调用会变慢,请结合使用 Invoke 和 InvokeRequired。"?
  • @Jordy:我不会那样做。不是因为过早的优化,而是因为 InvokeRequired 和 Invoke 的组合在调用线程可以是任何东西的情况下是一种固定模式。
【解决方案2】:

在实践中,人们倾向于从外部线程和拥有线程调用相同的方法。通常的模式是方法本身确定线程是否是拥有线程。如果是,则执行后续代码。如果不是,那么这次使用Invoke 调用自己的方法。

这样做的一个好处是它使代码更紧凑,因为您有一个与操作相关的方法而不是两个。

另一个可能更重要的好处是它减少了引发跨线程异常的机会。如果这两种方法在任何时候都可用,并且两个线程都可以选择两者中的任何一种,那么看似合法的方法调用就有可能引发异常。另一方面,如果只有一种方法可以适应这种情况,它会提供更安全的界面

【讨论】:

  • 但在我看到的大多数代码示例中,它被用作 SetTextBox(string text)。但是不必使这些方法已经表明您正在尝试从 UI 线程外部访问它吗?在我看来,当您想从 ui 线程更改 TextBox 时,您可能只需调用 TextBox.Text
  • @Jordy 这样做的重点是,它允许您在调用该方法时不关心您是否在 UI 线程上(即使在给定的调用点,您会确定) .它完全将检查留给方法本身。必须制作这样的方法仅意味着您处于潜在跨线程场景中,而不是在任何给定时间您都必然从错误的线程调用。跨度>
  • 我想我开始明白你的意思了,但是如果你想让你的代码线程安全,像锁定这样的东西难道不是更好的解决方案吗?
  • @Jordy 如果您在谈论如何实现 WPF 控件本身可以,那么是的,对于使用它们的程序员来说,锁可能会更方便。如果您正在谈论使自己的代码线程安全鉴于控件是它们的方式,我看不出锁会有什么帮助,因为您不是在处理“无助" 资源,但是已经有了某种安全性(这是一种你无法控制的安全性,所以理论上你可以让它与你的锁合作)。
  • 我认为您在回答中谈到的异常是由线程不安全的代码引起的异常。我刚刚发布了我想到的针对线程不安全的拳头解决方案。我现在明白你的意思了。
猜你喜欢
  • 1970-01-01
  • 2011-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-22
  • 1970-01-01
  • 2020-09-07
  • 1970-01-01
相关资源
最近更新 更多