【问题标题】:Multiple threads invoking the UI thread: locking needed for form data?多个线程调用 UI 线程:表单数据需要锁定?
【发布时间】:2013-04-12 22:44:32
【问题描述】:

我没有问题(目前),因为我的应用程序可以运行,但我想了解发生了什么,这样我以后就不会遇到麻烦了。 (我主要是数据库/Web 应用程序程序员,所以线程通常不是我的事!)

短版:多线程更新表单数据时,UI是否需要锁定?

我有一个简单的 Winforms 应用程序(使用 DataGridViews),它扫描文件中的某些内容并将结果显示给用户。起初我在 UI 线程中完成了这一切,但了解到这不是一个好主意,并决定将 ThreadPool.QueueUserWorkItem 用于 [ProcessorCount] 项目集合。这很好用,使用委托和 BeginInvoke 功能在找到结果时将结果发送到 UI。 (虽然有时结果太多,用户界面仍然滞后,但这是另一个问题。)

工作线程是完全隔离的,各司其职。我了解多线程的概念,并且需要注意同时访问共享数据。然而,我不完全清楚的是,当各种线程调用 UI 线程来更新它时会发生什么。只有 UI 会更新控件(和表单级变量),但由于调用来自其他线程,这将如何发挥作用?例如,如果我将项目添加到列表或增加计数器,来自一个工作线程的调用是否会中断另一个?每个 BeginInvoke 都是它自己的调用,但修改数据似乎仍然是个问题。

我发现没有一个 BeginInvoke 示例提到任何需要锁定 UI。这两个主题是相关的,但仍然没有给出我正在寻找的确切答案:

Using Control.Invoke() in place of lock(Control)

What's the difference between Invoke() and BeginInvoke()

【问题讨论】:

  • 为什么你应该从一个线程中完成所有 UI 操作:UI 线程。

标签: .net multithreading winforms locking begininvoke


【解决方案1】:

例如,如果我将项目添加到列表或递增计数器, 来自一个工作线程的调用可以中断另一个工作线程吗?每个 BeginInvoke 是它自己的电话,但修改数据似乎仍然可以 一个问题。

不,invoke 调用不能被另一个此类调用中断。事实上,它不能被 UI 线程上的any 其他操作中断。这就是如果你让你的 UI 线程工作 UI 本身“挂起”的原因(输入和绘制消息排队等待处理,但它们不能中断你正在做的工作)。

但是,将项目添加到 List<T>(您是这个意思吗?)或增加计数器不是 UI 操作。你为什么要在 UI 线程上做这些?

确实,在 UI 线程上调用此类操作会给您带来线程安全锁定的副作用,但它并不是真正免费的:它比在工作线程。

您应该考虑改用这种方法,这也将解决您的“更新太多 = 滞后”问题:

  • 数据变量(列表、计数器等)由工作线程直接操作。使用适当的锁定结构(lockInterlocked 方法、concurrent collections 等)来提供线程安全。
  • 每当工作线程修改数据变量时,它还会将全局modified 标志设置为true
  • 没有被编组到 UI 线程(调用)。
  • 同时,UI 线程设置了一个计时器,以在每个例如半秒。每当计时器触发时,UI 线程都会检查 modified 的值并将其重置为 false(您可以使用 Interlocked.CompareExchange 进行无锁操作)。
  • 如果 UI 线程发现数据已被修改,它会锁定所有数据变量并根据需要重新绘制 UI。

这样,您在每个计时器滴答声中最多只能获得一次(昂贵的!)UI 更新,并且无论从工作线程传入多少数据,UI 都不会滞后。另外,您可以根据自己的喜好选择计时器间隔的短或长。

【讨论】:

  • 谢谢,听起来我应该这样做。我在 UI 上添加列表/更改计数器只是因为它很方便而且我没有太多经验,我猜。 :) 我需要某些数据位用于 UI 操作(例如缓存结果),一旦我从其他线程获得它们,将它们直接存储在表单中似乎是有意义的。但正如您所建议的,工作线程可以轻松地将它们存储在其他地方以供 UI 检索。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-12
  • 2017-04-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多