我知道在 .NET 中,需要使用 Control.Invoke(delegate) 对控件执行操作。这让我想知道在哪些环境中实际上需要调用。据我所知,在 Visual Basic 和 Pascal 等旧版本中不需要它。特别是 Java(可能与版本有关?)和“旧式”Windows GUI 编程(手动读取消息队列)的状态如何?
4 回答
你误会了。Control.Invoke 不需要对控件执行操作。
Control.Invoke 是帮助跨线程边界编组代码的有用工具,但它不是唯一的方法。当你在后台线程中做一些工作时,你会看到这个最常用的。
当您从必须更新 Windowed 控件(即具有 Windows 窗口句柄并通过消息泵处理消息的控件)的后台线程调用代码、更改其属性或对控件进行一些其他操作时,您必须传递控制权到 MessagePump 线程,而 Control.Invoke 是实现这一点的有用方法。
在Windows 下运行的所有程序中,您不能调用在后台线程上更新控件的函数。一些语言可能会在后台处理这个细节,但我不知道有什么能做的。
在大多数语言中,调用 GUI 操作是必要的。在 Java 中有SwingUtilities.invokeLater。实际上,如果使用多个线程,则在任何环境中都需要这样的方法。原因是主 UI 线程运行了一个事件通知机制。能够与此机制交互的第二个线程需要以某种方式与之同步。
这些调用方法实际上是为开发人员提供便利。在不存在的平台中,这并不意味着允许从不同的线程访问 UI 项。这可能只是意味着开发人员应该实现一个自定义机制(发送事件)来这样做。UI 代码很少是线程安全的。我使用过许多平台和技术,并且始终遵循这条规则:仅从单个线程触摸 UI。
Invoke 只是 Win32 API PostMessage 的包装器。只有拥有窗口的线程才被允许访问该窗口。这是可以追溯到 Windows 单线程时的遗留问题。这意味着无论您使用什么(VB6、Delphi、.NET 或其他),PostMessage 都是在 Windows 上访问真实窗口的正确方法,但是不同的编程语言提供了包装器,使我们的生活更轻松。
实际上,不。仅当您的 GUI 运行超过 1 个线程时才需要它。对于 WinForm(和 WPF)应用程序,GUI 在单线程 STA 线程上运行(这可以追溯到 COM,不要问我为什么会这样,因为我真的不知道)。
如果您尝试从不同的线程调用在 STA 线程上创建的对象,则会进行检查以确保引发异常。WPF 也是如此,但 WPF 使它更加优雅。
无论如何,您实际上可以检查何时需要调用,因为有一个属性。建议使用此模式来帮助处理 WinForm 应用程序中的多个线程。
public void MyTextSetMethod(string text)
{
if(control.InvokeRequired)
{
control.Invoke(new Action<string>(MyTextSetMethod), text);
}
else
{
control.Text = text;
}
}
上面的代码提供了一个全方位的方法来设置 Text 属性,您可以根据自己的需要进行调整。