2010-07-05 26 views
1

我有一個類,其中包含(其中包括許多其他內容)需要儘可能經常保存到磁盤的項目列表。爲了做到這一點,無論何時做出任何改變,它都會使用一個定時器來延遲半秒的存儲時間,如果在半秒鐘之內發生任何進一步的改變,它會延遲定時器;換句話說,只要半秒內沒有變化,它就會保存到磁盤中。在單獨的線程上處理保存

但是,我遇到線程問題,其中列表可以在保存中修改。我想出了一個修復它的方法,但是感覺有點像黑客,而且我確信必須有更好的方法。

save方法在保存操作期間將線程鎖定在'object'的私有實例上,但這並不會停止正在更新的列表,因爲它是一個單獨的類,並且無法訪問該私有鎖。

所以,爲了解決這個問題,我所做的是向列表類添加一個'約改變'事件(不管它有什麼壞的特性),並且包含的​​類監聽這個事件,並鎖定與save方法在同一個私有鎖上,但不採取任何行動。這迫使線程等待,直到保存完成,然後允許更新列表,即使列表本身不知道它包含對象,或者它正在做什麼。

這裏的(嚴重簡化的)源:

public class ItemContainer 
{ 
    private ItemList _itemList = new ItemList(); 
    private object _padlock = new object(); 

    public ItemContainer() 
    { 
    _itemList.ItemAdded += StartTimedSave; 
    _itemList.ItemAdding += (sender, e) => { lock(_padlock) { } }; 
    } 

    private StartTimedSave() 
    { 
    // starts or resets a half second timer to call Save(); 
    } 

    private void Save() 
    { 
    lock(_padlock) 
    { 
     foreach (object obj in _itemList) 
     { 
     // save it. 
     } 
    } 
    } 
} 

public class ItemList 
{ 
    public event EventHandler<CancelEventArgs> ItemAdding; 
    public event EventHandler ItemAdded; 

    public List<object> _list; 

    public void Add(object obj) 
    { 
    if (ItemAdding.RaiseWithCancel(this)) // extension method 
     return; 
    _list.Add(obj); 
    ItemAdded.Raise(this); // another extension method 
    } 
} 

我不能逼真地具有列表保持對它的引用的包含對象,作爲列表是可以或可以不包含一個相當廣義類由這樣一個對象。

是一個空的lock { }塊的事件處理程序真的要走這條路嗎?我對允許事件處理程序干擾提升它的對象的操作的想法感到不舒服,但我看不到更好的方法。

+0

我想這個例子可能有點太簡化了;我再看看我的代碼,並且實際上存在的不僅僅是這一個可以在保存期間在外部修改的列表。我想我真正希望的是,有一種鎖定方式可以防止該進程中的任何其他線程運行,直到完成保存爲止,但我懷疑這是無法完成的。 – Flynn1179 2010-07-09 08:09:52

回答

2

保存時,在內存中創建一個副本並保存 - 這將減少數據暴露給其他線程中的更新。

是線程安全的,請ITEMLIST類來創建並返回一個列表複製和ITEMLIST類可以鎖住內部列表足夠長的時間,使複印件:

public List<object> CreateListCopy() 
{ 
    List<object> result = new List<object>(); 
    lock(_list) 
    { 
     foreach(object o in _list) 
     { 
      result.Add(o.Clone()); 
     } 
    } 

    return result; 
} 

然後保存結果CreateListCopy()在你的保存線程中。

編輯:當複製項目列表時,不要忘記確保您使用深層複製語義,因爲您不想僅複製參考;你想要完整的,完全獨立的數據副本。

+0

鎖定'_list'本身有什麼意義?否則,我可能會碰到同樣的問題,通過迭代來增加結果。 – Flynn1179 2010-07-06 13:19:52

+0

鎖定_list是因爲它是線程動作的實際目標 - 讓ItemList在內部處理鎖定使得更容易跟蹤哪些鎖定在哪裏。在這種情況下,我們不希望另一個線程在迭代時添加/刪除項目。 我會在ItemList的Add()和Remove()方法中使用類似的鎖 - 儘可能縮短鎖定的範圍。 – 2010-07-06 13:38:02

1

這無助:

鎖(_padlock){}當塊被執行

鎖持有 - 沒有一點空鎖塊。

如果所有問題都與該私人鎖定對象有關,則只需爲其公開獲取該對象。確保任何修改或讀取集合的東西都會鎖定它。

+0

嗯,它本身可能不會做任何事情,但它強制線程等待,直到Save方法中的鎖定塊完成。不幸的是,我不能公開它,因爲列表沒有提及包含它的對象。 – Flynn1179 2010-07-05 08:47:35

+0

我不確定編譯器不會優化空白塊。這是一個聰明的事情。 – alxx 2010-07-05 13:43:35

+0

它可能是,但不''鎖(obj){/ * code * /}'只是'Monitor.Enter(obj)的語法糖;/* code */Monitor.Exit(obj);'?在這種情況下,Monitor上的兩個靜態調用將會在那裏,而不會被優化。 – Flynn1179 2010-07-05 14:14:07

相關問題