您可以从另一个线程访问 UI 元素吗? (不设置)

Posted

技术标签:

【中文标题】您可以从另一个线程访问 UI 元素吗? (不设置)【英文标题】:Can you access UI elements from another thread? (get not set) 【发布时间】:2012-05-14 02:40:33 【问题描述】:

我在 google/here 上看到了很多关于从另一个线程更新 UI 元素的线程。

如果我只想获取复选框的值怎么办?

我能做到这一点而无需做任何特别的事情吗?

【问题讨论】:

你可以使用委托来做到这一点 @CodeCaster 只是尝试并查看有效的方法通常不会得到线程安全的代码。 @hvd:但它确实满足了这里提出的所有问题。 @hvd 没关系,我来不及编辑了。我只是想说我在这个问题上没有看到太多研究,比如searching。 嘿,实际上我希望我能多次投票,因为结果证明这是一个有趣的问题。从我下面的实验中可以看出,它适用于 WinForms,但不适用于 WPF。很有趣。 【参考方案1】:

编辑:看来我得收回之前写的东西了。尝试了以下方法:

添加了一个名为myTextBox 的文本框并尝试检索Text 属性的值:

Thread t = new Thread(
    o =>
    
        Thread.Sleep(2000);                    
        string value = myTextBox.Text;
        Thread.Sleep(2000);
    );
t.Start();

似乎应用程序 (WPF) 在 2 秒后崩溃。使用调度程序工作:

Thread t = new Thread(
    o =>
    
        Thread.Sleep(2000);
        myTextBox.Dispatcher.BeginInvoke(
            (Action)(() =>  string value = myTextBox.Text; ));
        Thread.Sleep(2000);
    );
t.Start();

因此,在从 GUI 组件读取值时,您仍然需要通过调度程序线程,至少在 WPF 中是这样。

第二次编辑:这会变得更好。显然重复经典 WinForms 的实验表明它可以在不使用 Invoke/BeginInvoke 的情况下读取 Text 属性。有趣的是,似乎设置属性也可以正常工作(无需调用),尽管我敢打赌它不是线程安全的,并且应用程序不会出于某种原因抱怨。

底线:在任何情况下,在与来自其他线程的 GUI 组件交互时使用调度程序都是一个好主意,因为它可以确保读取/写入被序列化到单个线程,因此您有没有线程安全问题。

【讨论】:

“应用程序不抱怨” -> 也许在发布模式下运行? @Henk Holterman:我已经尝试过 Release 和 Debug,但似乎没有发现任何区别。 @Geesu:您使用的是 WPF 还是 windows 窗体? Dispatcher 属性仅存在于 WPF 中。在 Windows 窗体中,您必须直接使用 textBox.BeginInvoke(...) 奇怪的是现在我得到“在创建窗口句柄之前不能在控件上调用 Invoke 或 BeginInvoke。”当我尝试访问它时,我知道表单已创建。即使我可以在不执行 BeginInvoke 的情况下打印出值 @Geesu:我建议你在另一个问题中使用你的代码发布这个问题,以便其他人也可以看到它。【参考方案2】:

您可以从另一个线程访问 UI 元素吗? (不设置)?

没有。

这是交易。 UI 元素有非常严格的线程亲和性要求。这意味着您只能从托管它的线程访问该元素。这包括各种访问,包括简单读取。1

对于简单的属性获取器来说它可能工作得很好,但它感知到的安全性将是特定控制实施方式的意外结果。由于Control 实例具有线程亲和性,它们可能会使用线程本地存储技术来保存它们的一些状态,这些状态当然与不同的线程不兼容。或者,如果您尝试读取的值处于半生不熟的状态怎么办?由于写入可能发生在您无法控制的代码中,因此无法同步对该读取的访问。这仍然忽略了可能出现的细微内存屏障问题。

再说一次,如果它看起来有效,那就把它归结为意外。从线程访问 UI 元素而不是从线程访问它们是灾难的根源。事情可能会出人意料地失败。


1这条规则很少有例外。使用ISynchronizeInvoke 方法就是这样一种例外。

【讨论】:

【参考方案3】:

你可以但严格来说它不是线程安全的。 例如,如果属性 Get 代码包含多个操作,则 UI 线程可能会在 Get 操作的中途同时进行操作,从而导致意外结果。

【讨论】:

【参考方案4】:

像往常一样简单地读取值。只有更新控件,才需要切换到GUI线程。

【讨论】:

可能适用于 CheckBox.Checked '因为它是一个布尔属性。正如其他人发布的那样,获取 textbox.Text 更加狡猾。

以上是关于您可以从另一个线程访问 UI 元素吗? (不设置)的主要内容,如果未能解决你的问题,请参考以下文章

从另一个线程访问 UI 线程的视图

MFC:从另一个线程访问 GUI?

如何在多个线程中访问 UI 元素?

我可以异步创建和绑定 ViewHolders 吗?

单击另一个元素时,您可以触发 jQuery UI 选择菜单吗?

Java:如何从另一个线程启动 UI 对话框,例如对于身份验证器