【问题标题】:ISynchronizeInvoke vs SynchronizationContext vs mainForm.InvokeISynchronizeInvoke vs SynchronizationContext vs mainForm.Invoke
【发布时间】:2014-09-24 15:11:57
【问题描述】:

我有一个 Worker 类和一个 MainForm/UI 类。从 UI 类中,我在新的后台线程中创建了 Worker 类的新实例。该线程将一些更新编组回 UI 的控件。由于它们在不同的类中,我基本上将 MainForm 实例 (this) 和适当的委托传递给工作类的构造函数以更新控件。在构造函数中,我将 mainForm 设置为 ISynchronizeInvoke 对象(称为 _synch),然后,在工作类的更下方,我执行 _synch.Invoke(theDelegate, new object[] { "new value" })

一切都很好,但后来我意识到也可以只使用mainForm.Invoke(不使用ISynchronizeInvoke 对象)。两者有什么区别?

更糟糕的是,我在一篇文章中读到 ISynchronizeInvoke 已经不再需要太多了,现在 SynchronizationContext 已经出现了很长时间。我意识到我不明白这两个是为了什么。对于理解为什么我应该在这些对象上使用 Invoke 而不是直接在 mainForm 上使用的任何帮助,我们将不胜感激。

【问题讨论】:

  • ISynchronizeInvoke 仅对不知道哪个特定线程是 UI 线程或如何正确调用操作 UI 的代码的另一个库有用。 Form 类从来都不是问题,它 UI。并实现使 ISynchronizeInvoke 工作的管道。遗憾的是,WPF 违反了合同,因此它不再是通用接口。
  • @HansPassant 谢谢汉斯。我尝试按照 Sriram 的建议切换到 SynchronizationContext,但 Send 和 Post 不接受自定义代表。他们采用只有一个参数 (object) 的 SendOrPostCallback 委托。我当前的自定义委托有一个值字符串(我希望标签采用的文本)和一个字符串 lbName,以便该方法可以识别要更新的标签控件。你建议我在这里做什么?我应该坚持使用 Form.Invoke 吗?谢谢!
  • 99% 的情况下使用 Invoke 都是错误的。非常容易出现死锁和竞争,请始终使用 BeginInvoke。单个对象足以存储任何东西,必要时使用小的帮助类。 lambda 几乎总是方便的选择。
  • @HansPassant BeginInvoke 在我的情况下不起作用,因为我在彼此下方有几个调用(在while(!mre.WaitOne(50)) 循环中更新不同的标签。所以如果我使用 BeginInvoke,标签会结结巴巴,而不是更新顺利。我无法给出一个很好的技术解释为什么。关于 SynchronizationContext 的话题,在这里做这个似乎是一个糟糕的决定,因为我必须创建一个前面提到的帮助器类(附加代码/复杂性),当我没有真正得到任何回报时。Form.Invoke 在这种情况下与 _synch.Send 具有相同的效果。你不同意吗?

标签: c# multithreading synchronization


【解决方案1】:

在 Winforms 中,无论您调用什么方法Form.InvokeISynchronizeInvoke.InvokeSynchronizationContext.Send,您都在做同样的事情。

实际上,它们都在内部使用相同的方法,即Control.Invoke(实现ISynchronizeInvoke 接口成员)。

两者(Form.Invoke 和 ISynchronizeInvoke.Invoke)有什么区别?

什么都没有,它们指向相同的方法Control.Invoke

也就是说,如果您依赖ISynchronizeInvoke,当您将应用程序移植到另一种技术(例如 WPF)时,您会感到痛苦。那里不支持ISynchronizeInvoke。这是 Winforms 特有的东西。您应该始终偏爱SynchronizationContext

SynchronizationContext 提供抽象,无论技术是什么,您都可以通过SynchronizationContext 编组对 UI 线程的调用(通常不总是)。如果您的代码依赖于SynchronizationContext,您可以轻松移植到 WPF 或 Asp.Net,因为它们提供了 SynchronizationContext 的技术特定实现。

  • Winforms 实现 - WindowsFormsSynchronizationContext
  • Wpf/Silverlight 实现 - DispatcherSynchronizationContext
  • Asp.net 实现 - AspNetSynchronizationContext

SynchronizationContext 的一个缺点是它无法获取返回值,尽管您可以通过闭包、实例成员等解决它。

延伸阅读:It's All About the SynchronizationContext

【讨论】:

  • 谢谢!所以我会忘记 ISynchronizeInvoke。关于仅使用 Form.Invoke,缺点是如果我切换到 WPF,Form.Invoke 将不起作用(即使那里的控件具有相同的名称)?
  • 如果我尝试使用 SynchronizationContext 作为对象,我会在构造函数中收到此错误:Cannot convert source type MainForm to target type SynchronizationContext。有什么想法吗?
  • @Anders 您不能在 wpf 中使用相同的控件。您将使用 wpf 特定的等效控件。虽然你可以在 wpf 中使用Dispatcher.Invoke,但这意味着你需要修改你的代码。如果使用了SynchronizationContext,它将按原样工作。
  • 不能这样赋值,需要在创建表单后使用SynchronizationContext.Current属性。参考这个codeproject.com/Articles/31971/…
  • 啊,谢谢。委托可以只有一个参数吗?在 Invoke 中,我使用了一个委托,该委托具有一个字符串值和一个作为字符串的标签名称,该委托被发回,以便 UI 线程可以更新其正确的控制。现在我收到delegate is not assignable to parameter type SendOrPostCallback 的错误。再次感谢!
猜你喜欢
  • 2011-09-14
  • 2014-08-17
  • 2011-09-18
  • 1970-01-01
  • 2012-07-27
  • 1970-01-01
  • 2010-12-09
  • 2017-06-20
相关资源
最近更新 更多