2012-03-13 97 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排隊的過多數量的掛起操作。可能會有一些重複的更新,但通常不會太多。在某些情況下,讓控件的代碼限制每秒執行的更新次數可能會有所幫助;如果更新例程將循環太多次,則啓動一個定時器並禁用更新,直到定時器到期。如果定時器到期並需要更新,請執行更新並重新啓動定時器;如果過期並且不需要更新,則取消定時器。

試圖對集合進行每一次更改都會反映在「更新」事件中,這樣做容易起反作用。只要確保顯示的最後一次更新完全發生在集合的最後一次更改之後。