控件引发的事件是否应该在UI线程上?

时间:2012-05-05 01:25:08

标签: .net winforms multithreading events

我知道当你是调用者,调用方法或以其他方式操作控件时,你应该调用UI线程(或者拥有该控件的线程)来执行此操作。

但是,当控件通过其中一个事件进行回调时,可以安全地假设您正在调用正确的线程吗?

根据我对常用控件的体验,这总是正确的,但也许这只是因为大多数事件都是用户交互的结果,因此Windows消息由主消息循环处理,在UI线程上。

最近我遇到了一个我自己的自定义控件的问题,这个控件调用事件的原因不是为了响应用户交互,有时在后台线程上这样做。在一种情况下,事件的事件处理程序试图操纵另一个生成非法跨线程调用异常的控件。

我可以通过检查我的事件处理程序中是否需要调用来解决问题,但是我很想知道这里实际上是谁“有错”。

我无法在任何地方找到任何关于控件事件或最佳实践的“规则”的文档。有谁知道吗?或者,在您看来,控制权是否应该在正确的线程上呼叫订阅者或者用户有责任进行检查?

编辑:似乎没有人听说过任何记录的约定,但通常同意在控件的拥有线程上调用Control派生类上的公共事件是一个好主意,以避免对消费者。

3 个答案:

答案 0 :(得分:3)

  

我知道当你是来电者时,可以调用方法或其他方法   操纵控件,你应该调用UI线程(或线程   无论如何都要拥有这种控制权。

嗯...有争议的。当然,您永远不应该尝试从托管它之外的线程访问任何UI元素。但是,使用像Invoke这样的编组技术并不总是更新UI元素的最佳机制,特别是当您想要做的是使用工作线程中的进度信息更新UI时。别弄错我的意思。有一个时间和地点使用编组操作是完全合理的,但很多时候对于需要通过System.Windows.Forms.Timer更新自身的数据进行UI线程轮询,可以获得通常更简单,更高效的解决方案,和优雅(如果不是更多)一样。

  

然而,当一个控件通过其中一个事件回调时,   假设你被正确的线程调用是否安全?

不一定。我的意思是通常情况尤其如此Control个实例。但请记住,您也可以在表单上删除Component个实例。其中许多人在其他线程上引发事件。将BackgroundWorker.DoWorkSerialPort.DataReceived视为令人满意的反例。

  

我可以通过检查是否调用来解决问题   我的事件处理程序中有必要,但我很想知道是谁   实际上在这里“有过错”。

我将不得不说错误在于你。如果您的控件确实是子类Control,那么我会尝试非常以确保在UI线程上引发所有事件。如果你不这样做,肯定会混淆其他开发人员(对或错)认为他们 在UI线程上。如果您的“控件”只是子类Component,那么您可能没问题,但请确保记录组件的行为。

答案 1 :(得分:3)

在这里避免假设黑魔法,你描述了理智的结果。如果从工作线程引发事件,那么事件处理程序当然也将在该线程上运行。如果您在该处理程序中设置UI控件的属性,那么您将被提醒您,这不是合法的事情。

完全相同的机制在控件引发的事件中起作用。它们根据Windows发送的通知消息进行操作,以便在泵送消息循环的线程上发生。总是你在Winforms或WPF应用程序中的主线程,除非你做一些不明智的事情,比如在工作线程上创建窗口。因此,在“正确”的线程上引发这些事件并更新控件的属性不是问题。

事件不会从一个线程跳到另一个线程,除非您明确地编写代码来执行此操作。开始/调用(),你已经知道了。

注意ab /使用InvokeRequired,它是一种反模式。事先不知道特定代码运行的线程是不健康的。比如,您在工作程序上执行长时间运行的dbase查询,以避免冻结UI。因此,当其结果可用时,您知道您将不得不调用以显示结果。使用InvokeRequired没有意义,除了能够告诉你确实存在问题。确保在返回false时抛出异常。

答案 2 :(得分:0)

对我来说,如果我是你的控件的消费者,并且使用它开始抛出线程异常,我可能会有点恼火:)我想我会在检查事件被提升的营地,不取决于订户。