用户操作
[留言]  [发消息]  [加为好友] 
订阅我的博客
XML聚合    FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
xushiweizh的公告
<p>本博客内容除非特殊说明均属原创,如需转载、引用其中的部分文字,请注意以下几点: <p>1)请在转载(引用)的内容开始添加本人署名,并提供本博客中相应文章的链接。如你的作品为非电子读物或纯文本,请给出链接的url。 <p>2)请勿用于商业用途。 <p>3)如果愿意,请给我邮件:<a href="mailto:xushiweizh@gmail.com">xushiweizh@gmail.com</a>,让我知道我的东西到哪去了。谢过。 <p> <!-- Link --> <h3 class="listtitle">重要链接</h3> <ul class="list"> <li class="listitem"><a href="http://code.google.com/p/winx" target="_blank" onClick="javascript:urchinTracker('/outgoing/winx.googlecode.com');">WINX下载</a> <li class="listitem"><a href="http://groups.google.com/group/winx" target="_blank" onClick="javascript:urchinTracker('/outgoing/group');">WINX论坛</a> <li class="listitem"><a href="http://blog.csdn.net/xushiweizh/archive/2006/11/08/1372325.aspx" target="_blank">WINX之FAQ</a> <li class="listitem"><a href="http://www.winxgui.com" target="_blank" onClick="javascript:urchinTracker('/outgoing/winxcn');">WINX官方网站</a> <li class="listitem"><a href="http://blog.csdn.net/xushiweizh/archive/2006/11/14/1382676.aspx" onClick="javascript:urchinTracker('/outgoing/join-us');">WINX团队</a> </ul> <!-- Stat --> <script src="http://www.google-analytics.com/urchin.js" type="text/javascript"> </script> <script type="text/javascript"> _uacct = "UA-723632-12"; urchinTracker(); </script> <!--- Stat 51.la ---> <script language="javascript" type="text/javascript" src="http://js.users.51.la/665479.js"></script>
文章分类
WINX团队
ebasil的专栏(RSS)
VisualFC/WINX专栏(RSS)
任风行(一路奔跑)(RSS)
绅士亦花心之WINX相关(RSS)
许伟群的专栏(RSS)
友情链接
QuChunYu的专栏(RSS)
QWL1996的专栏(RSS)
Sting的专栏(RSS)
SunHui的专栏(RSS)
不亦快斋(RSS)
于无声处(RSS)
手机开发论坛
珠穆朗玛(老汉)(RSS)
福&柯实验室(RSS)
存档

原创  关于 lockfree 算法 收藏

lockfree的本质是乐观锁。也就是说,它假设多数情况下,别人不会改变。一个通用的lockfree算法可描述如下:
 
lockfree_modify(DataT* data)
{
    for (;;)
    {
        Save old state of data to a local variable;
        do modify;
        lock {
            if (current state == old state)
                commit modify & return;
        }
    }
}
 
可以看出,lockfree也是锁,只是将锁限制在一个最小的范围内(通常是一个原子操作)。由于仍然有锁,lockfree在多核下并不会比普通的锁高明多少,它也不能随cpu个数增加而获得呈线性scale的性能提升。
 
不过,lockfree有个最大的好处,就是不可能有死锁。因为对上面算法分析你可以知道,在某个线程modify失败,总意味着有另一个人成功了,整个程序总在一步步前进,而不是出现相互等待而产生饥饿的状况。
 
我们以stack为例,展示下lockfree的stack是怎样的:
 
class stack
{
public:
 struct Node
 {
  Node* prev;
 };
 
private:
 Node* m_head;
 
public:
 stack()
  : m_head(NULL)
 {
 }
 
 void push(Node* val)
 {
  for (;;)
  {
   Node* head = m_head;
   val->prev = head;
   if (InterlockedCompareExchangePointer((PVOID*)&m_head, val, head) == head)
    return;
  }
 }
 
 Node* pop()
 {
   for (;;)
   {
     Node* head = m_head;
     if (head == NULL)
       return NULL;
     if (InterlockedCompareExchangePointer((PVOID*)&m_head, head->prev, head) == head)
         return head;
   }
 }
};
 
嗯,看起来挺不错,代码还算优雅。。。不过遗憾的是,严谨的说其实上面的lockfree算法是有问题的。问题在哪里?在pop算法上。我们看lock范围内的语义:
 
     if (InterlockedCompareExchangePointer((PVOID*)&m_head, head->prev, head) == head)
         return head;
 
这句话用伪代码描述是:
 
     lock {
         if (m_head == head) {
              m_head = head->prev;
              return head;
         }
     }
 
问题在于,m_head指针的值没有发生变化,和整个stack没有发生变化,这两者等价吗?
 
不等价。完全可以发生一种情况就是,另一个线程执行了这样一段代码:
 
    v1 = pop();  // v1是m_head
    v2 = pop();
    push(v1);
 
此时,m_head没有变化,但是m_head->prev不再是v2,而是v2->prev了。
 
那么怎么办?在说解决方案前,我们先再聊下上例中的stack::push。既然stack::pop有问题,那么stack::push呢?我们说,push算法的并没有什么问题。尽管同样的,m_head指针的值没有发生变化,并不意味着整个stack没有发生变化。但是对于push来说,它只关心m_head,而不关心其他东西,所以stack是否发生变化对其并无影响。
 
那么,应该如何解决pop算法遇到的问题?一个比较通用的方法,就是版本号。lockfree算法中,有一个术语叫tagged_ptr,其实本质上就是一个打了版本号的pointer:
 
    struct tagged_ptr
    {
        void* p;
        size_t tag;
    };
 
每次要对p进行修改,都++tag。这样,判断是不是modify,只需要判断tag是否变化即可。
 
lockfree小结:

  • 优点:不发生死锁。性能通常比lock好一些。
  • 缺点:仍然是锁。所以并不能随cpu个数增加而获得呈线性scale的性能提升。
  • 缺点:lockfree算法的实现通常比较复杂,不利于推广到任意算法的lockfree改造。通常只有少量库代码使用lockfree。

发表于 @ 2009年05月02日 14:32:00 | 评论( loading... ) | 编辑| 举报| 收藏

旧一篇:计划在C++建立仿Erlang式的分布式并行机制 | 新一篇:C++良好代码风格之我见 - 兼谈boost的工程实用价值

  • 发表评论
  • 评论内容:
  •  
Copyright © xushiweizh
Powered by CSDN Blog