2012-12-20 43 views
3

让我们假设在实例化窗体/控件/元素(通常是主线程)不修改/在同一时间访问该元素的线程中运行的代码,是有可能:如果线程不修改该元素,是否可以从另一个线程访问UI元素?

  1. GET TextBox的Text属性。

  2. 枚举ListView。

  3. 订阅Form's Closing事件。 (知道挂钩将从实例化该窗体的线程调用)

我试过所有3,程序似乎没有抱怨它。我一直认为你必须调用任何想要甚至远程触摸与UI有关的任何调用(读或写)。

我非常清楚为什么我需要在修改元素时使用IsInvokeRequired/Invoke模式,但是我不明白为什么访问属性/事件会导致任何问题。

回答

5

然而,这绝对有可能导致意想不到的行为。另外,还需要考虑其他线程相关的错误,例如,竞赛状况/死锁见Managed Threading Best Practises

我总是坚持访问用户界面线程上的用户界面,以确保安全。

+0

因此,基本上,即使我遵循每一个线程最佳实践,仍然可能会出现一些无证的奇怪现象吗? –

+1

那么通过不在UI线程上访问UI组件,您的*不*遵循最佳实践。如果你开始讨论跨线程访问,它肯定会在某个时候炸毁你的脸,但不一定就是这样。 – James

+2

@Deli在一天结束时,所有重要的事情是一次只有一个线程访问一个'Control'。到目前为止,最简单的方法是只允许一个线程访问* any *控件,因为这样可以保证永远不会有另一个线程同时修改控件。如果您可以确保没有其他线程正在修改控件,您可以从非UI线程访问它,但由于GUI应用程序中涉及的复杂性,不建议这样做。 – Servy

3

你在做什么来确保UI线程在你读取时不会修改控件?整理UI线程的全部原因是,您不必担心这种情况。列举特别的列表框将会是最容易的,因为它会花费最长的时间(这为比赛条件创造了最大的窗口)。你应该编组所有3个这些东西的UI线程。

2

这并不安全。 UI线程可能(也可能会)在您从另一个线程读取控件时更改控件的状态。您的阅读可能会在半烤状态下收到控制权。它现在可能似乎在工作,但迟早会失败......可能是不可预知的,而且很壮观。

不,您可能不需要使用Invoke模式。坦率地说,这种模式通常是最糟糕的选择。通常情况下,让工作线程执行繁重的工作,然后通过队列将新数据或进度信息发送到UI线程,并让UI线程通过定时器选取它。这有几个优点。

  • 消除昂贵的InvokeBeginInvoke操作。
  • UI线程可以决定何时以及以何种频率更新自己的新数据,而不是由工作线程规定。
  • 由于不需要等待Invoke的返回,您可以在工作线程上获得更高的吞吐量。
  • 没有超出UI消息队列的风险,就像BeginInvoke那样。
  • 它解耦UI和工作线程交互。

您将需要枚举ListView并建立一个单独的数据结构,然后可以安全地由工作线程访问。如果ListView包含很多项目,请考虑与控件一起保持单独的收集。通过这种方式,您可以在更长的时间内处理数据应对的处罚,而这一时间可能不会被注意到。最后,我们不希望复制操作冻结UI线程,否则用户会注意到。 A ConcurrentBag等可能是一个不错的选择,因为它可以在工作线程正在读取时由UI线程安全地修改。

+0

'Invoke'和'BeginInvoke'并不是那么昂贵......事实上,有一个单独的专用计时器可能会占用更多资源。对于你的第二点,如果会有更多的更新,你可以缓冲更新。对于3,如果这是上下文中的问题,那么可以使用非阻塞版本,对于4,再次,它可以通过缓冲输出来解决,而5是特别好的一点。尽管如此,我更喜欢用不同的方法来解决这个问题。对于更高版本,您可以使用“IProgress”界面。 – Servy

+0

@Servy:你的观点都很好,很好。有些事情可以减轻我的观点。最后......当我走到“ISynchronizeInvoke”路线时,代码总是变得嗅到。 –