关于 lockfree 算法[通俗易懂]

关于 lockfree 算法[通俗易懂]lockfree的本质是乐观锁。也就是说,它假设多数情况下,别人不会改变。一个通用的lockfree算法可描述如下: lockfree_modify(DataT*data){   for(;;)   {       Saveoldstateofdatatoalocalvariable;       domodify;       lock{           

大家好,又见面了,我是你们的朋友全栈君。

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。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/161719.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Windowsapp_windowsserver是什么

    Windowsapp_windowsserver是什么WindowsServerAppFabric扩展了WindowsServer以为Web应用程序和中间层服务提供增强的托管、管理和缓存功能。AppFabric托管功能向Internet信息服务(IIS)、WindowsProcessActivationService(WAS)和.NETFramework4添加了服务管理扩展。其中包括托管服务和托管管理工具,

    2022年10月17日
    3
  • Ubuntu命令备忘

    Ubuntu命令备忘

    2021年7月29日
    55
  • 虚函数后面的分号可有可无_虚函数需要实现吗

    虚函数后面的分号可有可无_虚函数需要实现吗const 和 =0要分开理解.首先理解一下分开的意思成员函数后面用 const 修饰,const表示this是一个指向常量的指针,即对象成为一个常量,即它的成员不能够变化.(默认情况下,this的类型是指向类类型非常量版本的常量指针。例如在Sales_data成员函数中,this的类型是Sales_data *const,即类一旦实例化一个对象后,this指向这个对象,是不能改变的,但是对象…

    2022年8月18日
    7
  • 扫描web漏洞的工具_系统漏洞扫描工具有哪些

    扫描web漏洞的工具_系统漏洞扫描工具有哪些十大Web漏洞扫描工具AcunetixWebVulnerabilityScanner[(简称AwVS)AwVS是一款知名的Web网络漏洞扫描工具,它通过网络爬虫测试你的网站安全,检测流行安全漏洞。a)、自动的客户端脚本分析器,允许对Ajax和Web2.0应用程序进行安全性测试b)、业内最先进且深入的SQL注入和跨站脚本测试c)、高级渗透测试工具,例如HTTPEditor和HTTPFuzzerd)、可视化宏记录器帮助您轻松测试web表格和受密码保护的区域e)、支持含有CAPT

    2025年8月30日
    10
  • 系统错误号:0x8007005[通俗易懂]

    系统错误号:0x8007005通常这个错误代码是错误的权限导致的,所以只要改变系统的安全设置就行了。下载这个文件SubInACL(SubInACL.exe)http://www.microsoft.com/downloads/details.aspx?FamilyID=e8ba3e56-d8fe-4a91-93cf-ed6985e3927b&displaylang=en安装这

    2022年4月7日
    287
  • autoconf 英文手册

    autoconf 英文手册1IntroductionAphysicist,anengineer,andacomputerscientistwerediscussingthenatureofGod.“SurelyaPhysicist,”saidthephysicist,“becauseearlyintheCreation,GodmadeLight;and…

    2022年6月4日
    35

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号