并行编程中的lock free技术

并行编程中的lock free技术lockfree(中文一般叫“无锁”,一般指的都是基于CAS指令的无锁技术)是利用处理器的一些特殊的原子指令来避免传统并行设计中对锁(lock)的使用。众所周知,锁在解决并行过程中资源访问问题的同时可能会引入诸多新的问题,比如死锁(deadlock),另外锁的申请/释放对性能也有不小的影响,当然最大的问题还在于使用锁的代码模块通常难以进行组合。lockfree的目标就是要消除锁对编程

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

lock free (中文一般叫“无锁”,一般指的都是基于CAS指令的无锁技术) 是利用处理器的一些特殊的原子指令来避免传统并行设计中对锁(lock)的使用。

众所周知,锁在解决并行过程中资源访问问题的同时可能会引入诸多新的问题,比如死锁(dead lock),另外锁的申请/释放对性能也有不小的影响,当然最大的问题还在于使用锁的代码模块通常难以进行组合。lock free的目标就是要消除锁对编程带来的不利影响。

C++大牛Andrei Alexandrescu (就是把template玩得炉火纯青的那个gg,《MODERN C++ DESIGN》的作者)的文章《Lock-Free Data Structures》是lock free方面的代表作,有兴趣的最好看一下,就当开阔思路吧。(btw, Andrei这哥们儿好像就是爱玩点bt的东西,呵呵)。

不过lock free本身也是目前各种并行解决方案中比较受争议的一种: 一来这项技术有点过于诡异,掌握起来颇有难度,不过另一方面,因为它是完全基于最基本的编程技术,所以并不依赖任何语言/平台,理论上应用面可以很广。

在并行编程方面,函数式的那些东西(比如ErlangHaskell之类的)算得上是另起炉灶,而lock free算得上是就地解决吧。所以各种方案其实也不矛盾,都是为人民服务嘛;)

个人对lock free的观点是这项技术不应该也不会大面积地应用在实际编程中,毕竟像这种高难度的东西还是有点曲高和寡。对于技术本身反正是见仁见智,爱用就用,不用拉倒呗。不过我想无论是否在实际当中使用lock free技术,了解和研究这项技术本身都会对理解并行编程有很大的帮助。

lock free的基础是CAS (Compare And Swap)函数,它的功能可以用如下的代码描述:

[cpp] 
view plain  
copy

  1. template <class T>  
  2. bool CAS(T* addr, T expected, T value) {  
  3.    if (*addr == expected) {  
  4.       *addr = value;  
  5.       return true;  
  6.    }  
  7.    return false;  
  8. }   

如果以前没有真正了解过lock free技术,可能会产生疑惑,这个函数对解决我们并行中的竞争问题能有什么帮助呢?如果你有这样的疑问,没问题,因为我第一次见到这个的时候也是一头雾水。不过实际上这个函数只是描述了Compare And Swap的执行过程,函数本身并不能直接被使用,只是伪代码描述而已,切记。不过现代处理器通常都实现了对应CAS功能的原子指令,比如x86汇编里面的“ CMPXCHG ”就提供了这样的功能,所以CAS的实现实际是平台相关的。文章里面给出了Linux下利用asm的实现,相较之下windows上面的实现可以简单一些,因为windows提供了一族以InterlockedCompareExchange开头的API来封装具体的实现,比如其中的InterlockedCompareExchange函数声明如下:

LONG

InterlockedCompareExchange(

    IN OUT PLONG  Destination,

    IN LONG  Exchange,

    IN LONG  Comparand

);

这里值得注意的是函数的返回值是原始的*Destination内容,并不是像上面的C++代码描述的那样会直接返回一个布尔值指示交换操作是否真正发生。所以返回值的工作必须由我们自己来完成。显然这里不能在函数开始处对*Destination和Comparand进行比较然后用if/else这样的分支来选择返回true还是false,因为那样的话就必须有一个lock来进行保护了。我们好不容易找到个办法来避免对lock的依赖,岂能又给绕回去了?这里的标准做法是用API调用的返回值与Comparand传入参数进行比较,因为API确保返回的是一个LONG类型的值对象,这个值始终都是存在于函数的栈上面,所以即便在比较之前发生中断并且实际的*Destination内容又被其它线程修改了,也并不影响此处的比较结果,当然也不会影响CAS函数的返回值了。代码实现如下:

[cpp] 
view plain  
copy

  1. template<class T>  
  2. bool CAS(T* addr, T expected, T fresh) {  
  3.    return expected == (T)InterlockedCompareExchange((LONG *)addr, (LONG)fresh, (LONG)expected);  
  4. }  

当然针对实际调用中T通常为指针的情况可以直接用InterlockedCompareExchangePointer来避免显示的类型转换,可以考虑再加一个偏特化的template,不过处理方法一样。

最近发现codeproject上有一篇文章分别用C++和C#实现了lock free的算法,不过很遗憾这个实现是有问题的。由此也可以说明并行程序设计特别是lock free确实不是一件容易的事情,连这样的文章都弄错了。看一下它的CAS函数实现:

[cpp] 
view plain  
copy

  1. bool CAS(pointer_t& dest,pointer_t& compare, pointer_t& value)  
  2. {  
  3.    if(compare.ptr==InterlockedCompareExchangePointer((PVOID volatile*)&dest.ptr,value.ptr,compare.ptr)) {  
  4.       InterlockedExchange(&dest.count,value.count);  
  5.       return true;  
  6.    }  
  7.    return false;  
  8. }  

这里的问题是函数里面用了两条语句来完成对目标对象的修改,虽然两条语句本身都是atomic的,不过在它们中间仍然可能发生中断,所以这个CAS函数并没有发挥预期的作用。实际上基于CAS语句的lock free技术的本质是对于任何数据的修改并不直接修改对象本身,而是先去修改目标对象的一份拷贝(copy),然后通过实现为atomic的一次交换操作将修改后的拷贝内容赋值给目标对象。所以CAS语句通常像下面这样使用:

[cpp] 
view plain  
copy

  1. do {  
  2.    pOldData = pDest;  
  3.    delete pNewData;  
  4.    pNewData = pDest->copy();  
  5.    // modify pNewData  
  6. while (!CAS(&pDest, pOldData, pNewData))  

其实类似的思路也应用到解决其它问题。如果还没想到点什么的话可以去找一份C++里面智能指针(smart pointer)的代码来看看,所以这其实也是异常安全(exception safe)编码的必备武器之一。

上面的copy操作效率比较低,所以牛牛们在具体应用中想出了各种方法来减小数据copy的粒度。不过无论如何,将CAS语句实现成多条需要读写原始dest数据的操作都是不正确的。此处一种可行的做法是使用类似InterlockedCompareExchange64之流的加强版CAS API来一石二鸟,具体实现上还有很多巧妙的解法,已超出本文的范畴,就此打住。

题外话:如果之前对异常安全(exception safety)编码有所了解,可能会发现和本文谈的lock free有相似的地方。因为在异常安全里面对资源的修改最好的方式并不是直接修改目标对象本身,也是先创建/修改一份副本对象,最后通过保证没有异常抛出的swap操作来修改目标对象的内容。不了解的话可以随便找一份智能指针(smart pointer,比如boost里面的shared_ptr)的源代码l瞧瞧里面是不是用到了很多次std::swap().

lock free为的是线程访问的安全,exception safety为的是保证在抛出异常情况下数据的安全,不过解决问题的基本思路却是一样的。


锁无关的(Lock-Free)数据结构——在避免死锁的同时确保线程继续

锁无关的数据结构与Hazard指针——操纵有限的资源

无锁队列的实现

无锁队列的实现-循环数组

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

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

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


相关推荐

  • 深度学习(GoogLeNet)

    深度学习(GoogLeNet)1GoogLeNet的介绍1.1GoogLeNet的简介GoogLeNet模型是由谷歌(Google)团队开发出来的卷积神经网络,它是2014年ImageNet挑战赛的冠军模型。相比于AlexNet模型,GoogLeNet模型的网络结构更深,共包括87层。尽管模型结构变得更复杂,但参数量更少了。GoogLeNet模型的参数量为5941552个,仅为AlexNet模型参数量的1/10。这主要归功于GoogLeNet创新性地采用了Inception模块。感兴趣的读者可以阅读原始顶会顶刊http://

    2022年8月14日
    5
  • QXDM的使用_QMediaPlayer

    QXDM的使用_QMediaPlayer如何从qxdm的log里面看到发送的数据包选中需要过滤的地方,右键”RefilterItems”选中LogPackets(OTA)点击OK,就把数据包都过滤出来了。 name里面ss相关的是补充业务,在这些请求之前都要有一个MM/CMServiceRequest

    2022年10月2日
    2
  • Typora的最后一个免费版本MD编辑器「建议收藏」

    Typora的最后一个免费版本MD编辑器「建议收藏」title:Typora的最后一个免费版本date:2022-05-1116:39:04tags:MDcategories:软件废话不多说,直接来我的仓库下载就可以了。利用Typora把图片添加到Hexo博客中安装插件。$npminstallhexo-renderer-marked–save2.再修改一下配置文件_config.yml,加入下面的配置。marked:prependRoot:truepostAsset:true3.在“Blog->s

    2022年9月23日
    2
  • 高等数学解题神器app_ubuntu cp命令

    高等数学解题神器app_ubuntu cp命令XSS在chrome上,需要先关闭xss保护反射型low对输入未做过滤$data=no_check($data);输入&lt;script&gt;alert(document.cookie)&lt;/script&gt;middle输入校验functionxss_check_4($data){//addsla…

    2022年9月23日
    1
  • python之pandas简单介绍及使用(一)「建议收藏」

    python之pandas简单介绍及使用(一)「建议收藏」一、  Pandas简介1、PythonDataAnalysisLibrary或pandas是基于NumPy的一种工具,该工具是为了解决数据分析任务而创建的。Pandas纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具。pandas提供了大量能使我们快速便捷地处理数据的函数和方法。你很快就会发现,它是使Python成为强大而高效的数据分析环境的重要因素之一…

    2022年9月27日
    2
  • python 下载m3u8视频「建议收藏」

    python 下载m3u8视频「建议收藏」https://leetcode-cn.com/problems/course-schedule/solution/ke-cheng-biao-by-leetcode-solution/F12,打开开发者工具,清除会话记录,然后刷新网页下载该文件,内容如下:获取ts文件名称筛选出以“.ts”结尾的行有些情况下可能是以其他格式的文件,比如png,下载后修改后缀即可或者筛除以“#”开头的行…

    2022年6月20日
    25

发表回复

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

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