首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >WinForms多线程绑定场景,最佳实践?

WinForms多线程绑定场景,最佳实践?
EN

Stack Overflow用户
提问于 2009-03-02 15:25:28
回答 8查看 5.6K关注 0票数 12

目前,我正在设计/重新设计一个应用程序的数据库部分,该应用程序大量使用winforms绑定和来自后台线程的更新(在> 100条记录上每秒更新一次)。

让我们假设应用程序是股票交易应用程序,其中后台线程监视数据更改并将它们放到数据对象上。这些对象存储在BindingList<>中,并实现INotifyPropertyChanged,通过将更改绑定到winforms控件来传播更改。此外,数据对象目前正在通过WinformsSynchronizationContext.Send将更改编组到UI线程。用户可以在UI中输入一些值,这意味着一些值可以从双方更改。而且用户的值不应该被更新夸大。

所以有几个问题浮现在我的脑海中:

  • 是否有一个通用的设计行会,如何做到这一点(数据库中的背景更新)?
  • 何时以及如何在UI线程上封送?
  • 背景线程与绑定/数据对象交互的最佳方式是什么?
  • 应该使用哪些类/接口?(BindingSource,)
  • ..。

UI并不知道有一个后台线程,可以更新控件,据我所知,在绑定场景中,UI不应该知道数据来自何处……您可以将后台线程视为将数据推送到UI的东西,因此我不确定后台工作人员是否是我正在搜索的选项。

有时,您希望在数据/业务对象的操作期间获得一些UI响应(例如,在重新计算期间设置背景)。在绑定到背景的状态属性上引发一个已更改的属性是不够的,因为控件在计算完成后重新绘制吗?我的想法是挂接属性更改事件,并在控件上调用.update() .对此还有什么其他的想法吗?

EN

回答 8

Stack Overflow用户

回答已采纳

发布于 2009-03-03 12:20:33

这是一个棘手的问题,因为大多数“解决方案”都会导致大量定制代码和对BeginInvoke()System.ComponentModel.BackgroundWorker (这本身只是BeginInvoke的瘦包装)的调用。

在过去,我还发现您希望尽快延迟发送INotifyPropertyChanged事件,直到数据稳定为止。处理一个适当更改事件的代码通常需要阅读其他礼仪。您还经常有一个控件,每当许多属性的状态发生变化时,都需要重新绘制自己,而且您不会太频繁地使用该控件来重绘自己。

首先,每个自定义WinForms控件都应该读取它需要在PropertyChanged事件处理程序中自己绘制的所有数据,因此当它是WM_PAINT (OnPaint)消息时,它不需要锁定任何数据对象。控件在获得新数据时不应该立即重新绘制自己;相反,它应该调用Control.Invalidate()。Windows将将WM_PAINT消息合并到尽可能少的请求中,并且只在UI线程没有其他任务时才发送它们。这将使重绘的次数和数据对象被锁定的时间最小化。(标准控件通常使用数据绑定来实现这一点)

数据对象需要记录更改时更改的内容,然后,一旦完成了一组更改,“踢”UI线程到调用SendChangeEvents方法,然后调用PropertyChanged事件处理程序(在UI线程上)对所有已更改的属性进行调用。在运行SendChangeEvents()方法时,必须锁定数据对象,以阻止后台线程更新它们。

每当一组更新从数据库读取bean时,UI线程都可以通过调用BeginInvoke来“踢”。通常情况下,最好使用计时器进行UI线程轮询,因为只有当UI消息队列为空时才发送WM_TIMER消息,从而使UI感觉更响应。

还可以考虑完全不使用数据绑定,每次计时器触发时,让UI询问每个数据对象“更改了什么”。Databinding看起来总是不错的,但很快就会成为问题的一部分,而不是解决方案的一部分。

由于数据对象的锁定/解锁是痛苦的,并且可能不允许从数据库中读取更新,因此您可能希望传递数据对象的UI线程a(虚拟)副本。使数据对象是持久的/不可变的,这样对数据对象的任何更改都会返回一个新的数据对象,而不是更改当前的数据对象。

持久性对象听起来非常慢,但不必是,有关一些指针,请参阅。还可以查看堆栈溢出上的

还请看一下雷朗-。它的消息批处理可能是有用的。

(对于WPF,我将有一个View-Model,它设置在UI线程中,然后通过后台线程从多线程模型中以“批”的形式更新。但是,与WinForms相比,WPF在组合数据绑定事件方面要好得多。

票数 6
EN

Stack Overflow用户

发布于 2009-03-02 16:01:01

在这个主题上有一个特定的MSDN文章。但要做好准备,看看VB.NET。

此外,也许您可以使用System.ComponentModel.BackgroundWorker,而不是一般的第二个线程,因为它很好地形式化了与您所描述的生成的后台线程之间的交互。MSDN库中给出的示例相当不错,因此请查看它,以获得如何使用它的提示。

编辑:请注意:如果使用ProgressChanged事件与UI线程通信,则不需要编组。只要需要与UI通信,后台线程就会调用ReportProgress。由于可以将任何对象附加到该事件,因此没有理由进行手动编组。进度是通过另一个异步操作来传递的,因此不需要担心UI处理进度事件的速度,也不需要担心后台线程通过等待事件完成而相互交织。

如果您证明后台线程正在以太快的速度提高进度更改事件,那么您可能想看看UI更新的拉和推模型,这是一篇由Ayende撰写的优秀文章。

票数 2
EN

Stack Overflow用户

发布于 2009-03-03 08:09:00

是的,所有的书都显示线程结构和调用等。这是完全正确的等等,但是它可能是一个痛苦的代码,而且通常很难组织,这样你就可以为它做适当的测试。

用户界面只需要每秒刷新这么多次,因此性能永远不是问题,轮询也会正常工作。

我喜欢使用由后台线程池不断更新的对象图。他们检查数据值的实际变化,当他们注意到实际的更改时,他们会更新对象图根目录上的版本计数器(或者每个主项上更有意义的内容),并更新这些值。

然后,您的前台进程可以有一个计时器(默认情况下与UI线程相同)来触发一次左右,并检查版本计数器,如果它更改,则锁定它(以停止部分更新),然后刷新显示。

这个简单的技术将UI线程与后台线程完全隔离开来。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/602735

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档