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