1

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

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

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

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

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

使用 Control.Invoke() 代替 lock(Control)

Invoke() 和 BeginInvoke() 有什么区别

4

1 回答 1

4

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

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

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

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

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

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

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

于 2013-04-12T23:09:07.527 回答