C#中的ReaderWriterLock和LockFree Data Structure

前一阵在一个project中使用了ReaderWriterLock,发现了两个问题:
  1. Performance非常差
  2. UpgradeToWriterLock并不是atomic的从ReaderLock转换到WriterLock,而是等同于"lock.ReleaseReaderLock(); lock.AcquireWriterLock();"。这样的semantics有一定的迷惑性,我开始的时候也认为这个operation是atomic的,等出现bug并debug了很久才发现原来如此。不过经过认真的思考,发现这其实不是.NET designer的错,根本没办法把这个operation设计成atomic的。原因如下:
  • 很多个thread同时acquire到了ReaderLock,
  • 他们都call UpgradeToWriterLock,如果这个operation是atomic的,那么没有哪个thread能upgrade成功。

后来我干脆不用ReaderWriterLock了,直接换成了LockFree的方法。这个得益于ibm的一些paper。(http://www.research.ibm.com/people/m/michael/pubs.htm )
在C#中实现LockFree其实是很简单的,因为有了Garbage Collection,
code:
 1       class  LockFreeDictionary < Key, Value >
 2      {
 3          private Dictionary < Key, Value >  m_dict  =   new  Dictionary < Key, Value > ();
 4 
 5           public  Value Lookup(Key key)
 6          {
 7               return  m_dict[key];
 8          }
 9 
10           public   void  Update(Key key, Value value)
11          {
12              Dictionary < Key, Value >  newDict  =   null ;
13              Dictionary < Key, Value >  oldDict  =   null ;
14               do
15              {
16                  oldDict  =  m_dict;
17                  newDict  =   new  Dictionary < Key, Value > (oldDict);
18                  newDict[key]  =  value;
19              }  while  (Interlocked.CompareExchange < Dictionary < Key, Value >> ( ref  m_dict, newDict, oldDict) != oldDict);
20          }
21      }
22 

解释如下:
  1. line 16, keep a reference to the original Dictionary object,
  2. line 17, construct a new Dictionary object base on original object. For oldDict, this step is readonly, and doesn't need lock,
  3. line 18, perform the update operation upon the new constructed object,
  4. line 19, try to swap the new object into the original one. If the return value of Interlocked.CompareExchange operation is NOT equal to oldDict, it means during this do-while block executation, there is another thread changed m_dict. In this scenario, we need to do the update again.
  5. the swapped out object (oldDict) can be collected by Garbage Collection.
  6. if we want to use LockFree data structure in C++, there is another technique called Hazard Pointer. it's in the ibm research papers.
不过不是什么情况都可以使用这种LockFreeDictionary的, ,不然你会得到相反的效果(performance很差),这里的scenario是read非常多,write非常少。 不过这种情况也挺常见的。

这种方法的好处是在Lookup的时候没有任何lock,从而极大的提高了performance。(我的project里面比ReaderWriterLock提高了2000倍,

对LockFree有研究的或者有兴趣的可以留言大家讨论讨论,:)

你可能感兴趣的:(Writer)