淺談C# ReaderWriterLock
前一陣在一個project中使用了C# ReaderWriterLock,發(fā)現(xiàn)了兩個問題:
Performance非常差
UpgradeToWriterLock并不是atomic的從ReaderLock轉(zhuǎn)換到WriterLock,而是等同于"lock.ReleaseReaderLock(); lock.AcquireWriterLock();".這樣的semantics有一定的迷惑性,我開始的時候也認(rèn)為這個operation是 atomic的,等出現(xiàn)bug并debug了很久才發(fā)現(xiàn)原來如此。不過經(jīng)過認(rèn)真的思考,發(fā)現(xiàn)這其實不是。NET designer的錯,根本沒辦法把這個operation設(shè)計成atomic的。原因如下:
很多個thread同時acquire到了ReaderLock,
他們都call UpgradeToWriterLock,如果這個operation是atomic的,那么沒有哪個thread能upgrade成功。
后來我干脆不用C# ReaderWriterLock了,直接換成了LockFree的方法。在C#中實現(xiàn)LockFree其實是很簡單的,因為有了Garbage Collection,
code:
- class LockFreeDictionary<Key, Value>{
 - private Dictionary<Key, Value> m_dict = new Dictionary<Key, Value>();
 - public Value Lookup(Key key){
 - return m_dict[key];
 - }
 - public void Update(Key key, Value value){
 - Dictionary<Key, Value> newDict = null;
 - Dictionary<Key, Value> oldDict = null;
 - do{
 - oldDict = m_dict;
 - newnewDict = new Dictionary<Key, Value>(oldDict);
 - newDict[key] = value;
 - }
 - while (Interlocked.CompareExchange<Dictionary<Key, Value>>
 
(ref m_dict, newDict, oldDict) != oldDict);- }
 - }
 
第16行 ,保持參照原有Dictionary物件,
第17行,建造一個新的字典對象的基礎(chǔ)上原有的物件。為oldDict ,這一步是只讀的,而且不需要鎖,
第18行,執(zhí)行更新操作后,新建造的對象,
第19行,請嘗試更換新的對象到原來的1 。如果返回值Interlocked.CompareExchange操作不等于oldDict ,這意味著在此做,而塊executation ,有另一個線程改變m_dict 。在這種情況下,我們需要做更新一次。
換出的對象( oldDict )可以收集到的垃圾收集。
如果我們想用LockFree數(shù)據(jù)結(jié)構(gòu)C++中,還有另一種技術(shù)稱為危害指針。這是在IBM的研究論文。
不過不是什么情況都可以使用這種LockFreeDictionary的,,不然你會得到相反的效果(performance很差),這里的scenario是read非常多,write非常少。 不過這種情況也挺常見的。
這種方法的好處是在Lookup的時候沒有任何lock,從而極大的提高了performance.(我的project里面比C# ReaderWriterLock提高了2000倍,)
對LockFree有研究的或者有興趣的可以留言大家討論討論
【編輯推薦】















 
 
 
 
 
 
 