一个星期前,我写了关于SQL Server里闩锁(Latches)自旋锁(Spinlocks)的文章。2个同步原语(synchronization primitives)是用来保护SQL Server里的共享数据结构,例如缓存池里的页(通过闩锁(Latches)),锁管理器哈希表里的锁(通过自旋锁(Spinlock))。接下里你会看到越来越多的全新同步原语(synchronization primitives),即所谓的无锁数据结构(Lock-Free Data Structures)。那也是SQL Server 2014里建立内存中OLTP的一个基础,因此在今天的文章里我会给你快速概况介绍下无锁数据结构,还有它们提供了什么。

什么是无锁数据结构(Lock-Free Data Structures)

无锁算法通过非阻塞算法保护共享数据结构。在以前的关于闩锁(Latches)和自旋锁(Spinlock)文章里你已看到,当它们不能获得闩锁或自旋锁时,其他的线程会阻塞。当一个线程等待闩锁,SQL Server把线程放入挂起(SUSPENDED)状态,如果一个线程等待自旋锁,这个线程会在CPU上积极自旋。2个方法都会导致阻塞情形,这就是我们想通过非阻塞算法(Non-Blocking Algorithm)避免的。维基百科对非阻塞算法有个漂亮解释:

“A non-blocking algorithm ensures that threads competing for a shared resource do not have their execution indefinitely postponed by mutual exclusion. A non-blocking algorithm is lock-free if there is guaranteed system-wide progress regardless of scheduling.”

来看下中文字幕:

非阻塞算法保证为共享资源竞争的线程,不会通过互斥让它们的执行无限期暂停。如果有不管调度的系统级进程,非阻塞算法是无锁的。

从这个解释里得出的最重要的结论是一个线程不会被另一个线程阻塞。这是可能的,因为没有传统的锁是用做线程本身同步的。我们来看一个具体的例子:

让我们一步一步来分析这个代码。首先,函数compare_and_swap的实现是通过一个直接在CPU级别上的原子硬件指令(atomic hardware instruction)——CMPXCHG来实现。我想演示下在CMPXCHG里实现什么样的逻辑:你比较值与一个期望值,如果它们一样的话,老的值会赋予新的值。因为CHPXCHG的整个逻辑是在CPU本身上作为一个原子单元实现的,没有其他线程可以中断这个汇编运算码的执行。

为了存储自旋锁本身的状态,名为lock的变量被使用。因此线程在整个循环里自旋转,直到自旋转锁同步变量被解锁。如果这个发生的话,线程可以锁住同步变量,最后会进入在线程安全方式的临界区(critical section)。这又是自旋锁的简单演示(非线程安全)——事实上事情比这个难,且复杂很多。

这个传统方法的最大问题是,在线程同步里涉及到共享资源:自旋转锁同步变量lock。如果一个线程把持自旋锁,且挂起了,当其他线程尝试获取自旋锁就会卡在当型循环里。你可以通过引入无锁代码技术避免这个问题。

如你所见,Foo方法的实现已经完全改变了。不是尝试去获得自旋锁,实现方法在原子加进行前,只是检查是否有其它线程修改共享变量(原先是通过自旋锁受保护)。这就是说没有用到了共享资源,线程之间也不会彼此阻塞。这就是无锁数据结构(Lock-Free Data Structures)和非阻塞算法(Non-Blocking Algorithm)的主要思路。

在SQL Server 2014里的内存中OLTP也使用同样的方法来构建BW-TREE映射表的页改变。因此就不会涉及到锁,闩锁和自旋锁。如果内存中OLTP看到在映射表里的页地址以in个改变,这就是说另一个线程已经开始在那页上的修改——但还未完成(在CPU上同时有其它线程被调度)。在内存中OLTP里各个线程是相互合作来工作。因此如果线程看到映射表里的修改,就完成这个”挂起“操作是可能的——例如页分裂。

一个内存中OLTP的页分裂包含多个原子操作。因此一个线程可以开始一个页分裂,另一个线程可以最后完成这个页分裂。在以后的文章里我会讨论这些页分裂的更多信息,实现BW-TREE里改变,让这个复杂方法成为可能。

小结

在今天的文章里我向你介绍了无锁数据结构背后的主要思路。这个主要思路是在线程本身尝试执行一个原子操作前,检查是否有其它线程完成一个操作。因此不需要通过像自旋锁的同步原语来保护临界区。自SQL Server 2014起,无锁数据结构和非阻塞算法的思路已被内存中的OLTP使用。

感谢关注!

参考文章:

https://www.sqlpassion.at/archive/2014/09/29/lock-free-data-structures/

无锁数据结构(Lock-Free Data Structures)的更多相关文章

  1. 泛函编程(5)-数据结构(Functional Data Structures)

    编程即是编制对数据进行运算的过程.特殊的运算必须用特定的数据结构来支持有效运算.如果没有数据结构的支持,我们就只能为每条数据申明一个内存地址了,然后使用这些地址来操作这些数据,也就是我们熟悉的申明变量 ...

  2. C++ 无锁数据结构

    https://www.zhihu.com/question/52629893/answer/131731126

  3. 理解 Memory barrier(内存屏障)无锁环形队列

    原文:https://www.cnblogs.com/my_life/articles/5220172.html Memory barrier 简介 程序在运行时内存实际的访问顺序和程序代码编写的访问 ...

  4. 使用C++11实现无锁stack(lock-free stack)

    前几篇文章,我们讨论了如何使用mutex保护数据及使用使用condition variable在多线程中进行同步.然而,使用mutex将会导致一下问题: 等待互斥锁会消耗宝贵的时间 — 有时候是很多时 ...

  5. boost 无锁队列

    一哥们翻译的boost的无锁队列的官方文档 原文地址:http://blog.csdn.net/great3779/article/details/8765103 Boost_1_53_0终于迎来了久 ...

  6. 并发编程(三): 使用C++11实现无锁stack(lock-free stack)

    前几篇文章,我们讨论了如何使用mutex保护数据及使用使用condition variable在多线程中进行同步.然而,使用mutex将会导致一下问题: 等待互斥锁会消耗宝贵的时间 - 有时候是很多时 ...

  7. 并发编程入门(三): 使用C++11实现无锁stack(lock-free stack)

    前几篇文章,我们讨论了如何使用mutex保护数据及使用使用condition variable在多线程中进行同步.然而,使用mutex将会导致一下问题: 等待互斥锁会消耗宝贵的时间 - 有时候是很多时 ...

  8. 【DPDK】【ring】从DPDK的ring来看无锁队列的实现

    [前言] 队列是众多数据结构中最常见的一种之一.曾经有人和我说过这么一句话,叫做“程序等于数据结构+算法”.因此在设计模块.写代码时,队列常常作为一个很常见的结构出现在模块设计中.DPDK不仅是一个加 ...

  9. 无锁同步-JAVA之Volatile、Atomic和CAS

    1.概要 本文是无锁同步系列文章的第二篇,主要探讨JAVA中的原子操作,以及如何进行无锁同步. 关于JAVA中的原子操作,我们很容易想到的是Volatile变量.java.util.concurren ...

随机推荐

  1. Jquery easyui开启行编辑模式增删改操作

    Jquery easyui开启行编辑模式增删改操作 Jquery easyui开启行编辑模式增删改操作先上图 Html代码: <table id="dd"> </ ...

  2. React学习资料

    以下是我整理的React学习资料,包括:React基础.Redux.reat-router, redux middleware, higher order components, React验证等, ...

  3. ASP.NET Web API中的参数绑定总结

    ASP.NET Web API中的action参数类型可以分为简单类型和复杂类型. HttpResponseMessage Put(int id, Product item) id是int类型,是简单 ...

  4. Android版-支付宝APP支付

    此项目已开源 赶快来围观 Start支持下吧 [客户端开源地址-JPay][服务端端开源地址-在com.javen.alipay 包名下] 上一篇详细介绍了微信APP支付 点击这里 此篇文章来详细介绍 ...

  5. iOS 7.1 UITableView添加footerView 后 最后一行分割线无法显示

    今天用故事版 遇到个奇怪的问题: 我要用 tbView(tableView)展示写信息.最后一行我要显示些文案什么的.考虑用 footerView ,开心coding ..,show下 哪里有些不对吧 ...

  6. Javascript 严格模式

    简介 严格模式是一种将更好的错误检查引入代码中的方法. 在使用严格模式时,你无法使用隐式声明的变量.将值赋给只读属性或将属性添加到不可扩展的对象等. 声明严格模式 可以通过在文件.程序或函数的开头添加 ...

  7. 如何在使用MAMP环境下安装MySQLdb

    我的电脑上没有安装XAMPP,而是安装了MAMP PRO,其实两者都差不多,都是PHP+MySQL+Apache的集成环境,只是MAMP的GUI界面更华丽一些,但是也更复杂一些. 好了不说这些,说说问 ...

  8. Activity intent经常使用的 FLAG

    Intent.FLAG_ACTIVITY_NEW_TASK 默认的跳转类型,会重新创建一个新的Activity,不过与这种情况,比方说Task1中有A,B,C三个Activity,此时在C中启动D的话 ...

  9. Hadoop jar配置使用JMX进行远程JVM监控

    背景:  编写了一个MapReduce程序,发现该程序内存占用非常多,需要有一种方法来分析内存详细的占用情况. 可以使用linux上的pmap –d <PID>来看进程逻辑地址空间使用情况 ...

  10. 4kbps~15kbps语音编码器基础框架ACELP