关于 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。

转载于:https://www.cnblogs.com/wuwuwu/archive/2009/05/02/6162381.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/262522.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

枚举类enum

一、枚举类 package com.xxx.xf.common.enums;import com.xxx.xf.workday.contant.HolidayContant;/*** Author: wangmq* Version: 1.0* 操作枚举类*/ public enum XfOperateEnum {// 定义多个由构造方法组成的对象,逗号隔开分号为结束XF_OPERATE_SL("SL"…

js怎么调用wasm_Long.js源码解析

基于现在市面上到处都是 Vue/React 之类的源码分析文章实在是太多了。(虽然我也写过 Vite的源码解析所以这次来写点不一样的。由于微信这边用的是 protobuf 来进行 rpc 调用。所以有时候需要将 JS 中的 Number 类型转换为 Long 类型传给后端。目前用的最多的就是 Long.js 了&am…

设置UILabel可变高度(根据文本内容自动适应高度)

property(nonatomic)UILabel *showLabel;// 计算文本所占高度,计算出来之后设置label的高度// 第一个参数:字体大小,字体大小/样式影响计算字体的高度 // 第二个参数:CGSize结构体,结构体中第一个参数表示宽度&#xf…

励志英语谚语【二】

• All things in their being are good for something. • 天生我才必有用。 • Difficult circumstances serve as a textbook of life for people. • 困难坎坷是人们的生活教科书。 • Failure is the mother of success. -- Thomas Paine • 失败乃成功之母。 • For m…

AOP快速入门

一、概念 AOP面向切面编程,是函数式编程的延申,是对OOP的补充; 代理模式:拦截增强作用,增强功能; 1、java继承,纵向共性抽取, 2、横向切面AOP织入增强代码方式 二、原理是通过代理机…

图形大小_PS图形复制——等距复制、旋转复制、大小变换复制

PS中图形复制是很常见的事,可能不同人的习惯可能会用不同的方法。这里糖糖主要讲的是图形的一些等距复制、旋转复制、大小变换旋转复制,相信看过本篇文章之后你也可以通过图形的不同需要的复制做一些很不错的图案哦~糖糖先在PS里用钢笔工具绘制了一个绿叶…

在页面制作过程中需要注意事项

&#xff08;1&#xff09;静态页面.将信息页面和频道、网站首页改成静态页面&#xff0c;有利于搜索引擎更快更好地收录 &#xff08;2&#xff09;页面标题的关键词优化。必须列出信息的标题、网站的名称以及相关关键字 &#xff08;3&#xff09;<meta>标签优化。通过…

python 自动补全

cd ~/.vim/bundle git clone https://github.com/rkulla/pydiction.git cp -r pydiction/after ~/.vim转载于:https://blog.51cto.com/curran/1710868

net命令详解

许多 Windows NT 网络命令以 net 开始。这些 net 命令有一些公共属性&#xff1a; -通过键入 net /? 可查阅所有可用的 net 命令。 -通过键入 net help 命令可在命令行中获得 net 命令的语法帮助。 例如&#xff0c;要得到 net accounts 命令的帮助&#xff0c;请键入 net he…

AOP联盟通知类型和Spring编写代理半自动

一、cglib功能更强大 二、Spring核心jar包 三、AOP联盟通知 三、代码实现Spring半自动代理 1、环绕通知的切面 2、bean.xml配置 3、创建bean容器&#xff0c;获取bean&#xff0c;即已经创建好的代理对象&#xff1a; 4、配置多个接口 转载于:https://www.cnblogs.com/wmqiang/…

1到30图片大全顺序_终于解决了!自媒体作者,如何给 Word 中的图片批量编号?...

今天这个技巧非常有用&#xff0c;特别是对小编这种每天都要写图文教程的人来说&#xff0c;简直是续命神技。如果 Word 中有大量的图片&#xff0c;经常需要个每个图片顺序编号&#xff0c;有没有同款需求&#xff1f;大家平时都一个个手工输入的吗&#xff1f;编到后来很可能…

[状态压缩DP] COJ 1129 送货到家

第一道状态压缩DP&#xff1b; 这道题要求一个无向图的最小权回路&#xff0c;要求经过所有点&#xff0c;所以可以任选一个点&#xff08;这里选0&#xff09;作为起点&#xff0c;以后的状态f[s, i]表示从0出发到i结束的最小权路径&#xff0c;最终求得f[1<<n-1, k]后要…

模式8--WorkerThread

来至《java多线程设计模式》 类似ThreadPool&#xff0c;预先产生几个worker线程准备工作。 public void startWorkers() {for (int i 0; i < threadPool.length; i) {threadPool[i].start();}} package Sample;public class WorkerThread extends Thread {private final C…

Android Volley框架的使用(二)

此博文源码下载地址 https://github.com/Javen205/VolleyDemo.git 使用请求队列RequestQueue Volley中的Request都需要添加到RequestQueue中才能执行&#xff0c;所以首先需要创建一个RequestQueue RequestQueue Volley.newRequestQueue(mContext); 通常情况在一个应用中需要…

Spring全自动AOP和项目加入jar包

一、jar可以引进项目中&#xff0c;复制到路下后&#xff0c;要add as library&#xff0c;加载到工作空间中才能引入&#xff1b; 也jar包放在硬盘的项目目录外面&#xff0c;可以多个项目引入共用&#xff1a; 二、xml配置 1、aop全自动配置 2、xml装配bean,不是注解方式 3、…

迭代器和yield

2012-08-05©tc庄稼汉 迭代器是一种方法、get 访问器或运算符&#xff0c;它通过使用 yield 关键字对数组或集合类执行自定义迭代。 yield 返回语句会导致源序列中的元素在访问源序列中的下一个元素之前立即返回给调用方。 尽管您以方法的形式编写迭代器&#xff0c;但编译…

中怎样载入选区_ps中快捷大全

使用ps时使用快捷键更方便&#xff0c;操作起来更能提高工作效率&#xff0c;下面给大家总结一下&#xff0c;希望对大家有帮助&#xff0c;当然大家有更好的技巧可以来魔课66网一起交流一下文件设置:Ctrln——新建Ctrlo——打开Ctrlp——打印Ctrls—— 存储Ctrlw——关闭Ctrl …

AspectJ——AOP框架快速入门

一、导包 二、bean.xml配置 三、环绕通知 四&#xff0c;表达式 转载于:https://www.cnblogs.com/wmqiang/p/11617042.html

linux桌面环境应用

为什么80%的码农都做不了架构师&#xff1f;>>> 通常的 Linux 发行版都使用 KDE 或者 GNOME 作为默认的桌面环境。它们都给用户提供了一个原始的并且有吸引力的桌面&#xff0c;并且内置了各式各样的多媒体软件、系统程序、游戏、实用程序、网页开发工具、编程 工具…

[转]C++的坑真的多吗?

http://coolshell.cn/articles/7992.html#jtss-tsina 先说明一下&#xff0c;我不希望本文变成语言争论贴。希望下面的文章能让我们客观理性地了解C这个语言。&#xff08;另&#xff0c;我觉得技术争论不要停留在非黑即白的二元价值观上&#xff0c;这样争论无非就是比谁的嗓门…