2012-03-13 96 views
1

我正在使用Silverlight 5.0并需要实现IList<T>和IList。我的集合将用于不断添加和删除其内部集合中的项目,并且UI将具有绑定到集合的元素。出于性能方面的原因,我不希望UI在每次更改集合时都进行渲染,因为我希望对集合进行“组”更改,然后引发集合更改事件。我也希望能够使用Task.Factory方法来完成这一切,以保持一切异步。有没有人看到如何实现这一目标的好例子?异步,线程安全的集合实现INotifyCollectionChanged

+1

Caliburn Micro拥有一个可以关闭和打开的IsNotifying属性的Bindable集合。我没有在多线程互斥环境中使用它。但是,这是值得一试的。 – Jeremiah 2012-03-13 21:05:09

回答

0

线程安全的可观察集合在设计上存在问题。例如,如果更改集合并引发事件,则在处理该事件时,可以在另一个线程上更改集合。结果,刚刚被告知某个物品的处理程序已经过期(该物品可能已经不存在了)。

或者,您是否考虑在集合更改时使用带有事件的immutable collection?它本质上是线程安全的,如果正确应用它,不会受到上述设计问题的困扰。它可能不符合你对这个应用程序的要求 - 从这个问题很难说。

+0

处理程序可能在过期后调用的事实不应被视为问题。处理程序处理这些事情并不困难,并且没有理由不应该为他们准备一个体面的处理程序。更大的问题是当事件处理程序已经运行时,可能会将某些内容添加到集合中。通常,调用SomeCollection.Add的代码有权假定所有事件处理程序在返回之前都会触发,但强加此类语义会造成死锁的可能性。 – supercat 2012-12-31 20:00:52

0

我建议的方法是为可能需要更新UI的集合的不同方面设置各种标志。如果集合中的某些内容发生更改并且未设置特定的标志,请设置它并在提供的控件和委托上使用Control.BeginInvoke(使用Interlocked或锁定以确保标志测试和设置是以线程安全的方式完成的) 。 UI更新方法应该在执行更新之前测试并清除相应的标志;如果执行了任何更新,该方法应循环并重新测试所有标志,直到完成而不必执行任何更新。

使用这种方法,应该能够避免通过BeginUpdate排队的过多数量的挂起操作。可能会有一些重复的更新,但通常不会太多。在某些情况下,让控件的代码限制每秒执行的更新次数可能会有所帮助;如果更新例程将循环太多次,则启动一个定时器并禁用更新,直到定时器到期。如果定时器到期并需要更新,请执行更新并重新启动定时器;如果过期并且不需要更新,则取消定时器。

试图对集合进行每一次更改都会反映在“更新”事件中,这样做容易起反作用。只要确保显示的最后一次更新完全发生在集合的最后一次更改之后。