一个星期前,我写了关于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. mysql 创建用户并赋予权限

    mysql> create user 'zhangsan'@'localhost' identified by '123456'; Query OK, 0 rows affected (0.00 ...

  2. vs2013 下载

    http://download.microsoft.com/download/2/4/9/249BF223-5B84-4259-9424-429E66F45509/VS2013_RC_ULT_CHS. ...

  3. 高级屏幕空间反射: Screen Space Reflection (SSR)

    自从CE3首倡SSR以来,发展至今,其质量与当年早已不能同日而语.不仅强调超越性的质量,而且强调超越性的性能.乘着周末有空撸了撸,以下是增强型实时SSR结果图.与我原来的SSR原始实现相比,新的增强型 ...

  4. Sublime Text 使用技巧

    之前就一直在用Sublime Text 来作为默认的文本编辑工具,但也只是简单的用用,一些Sublime Text本身的快捷键什么的都没研究过,今天特地在网上看了一下,快捷键比较多,要想熟练运用还得在 ...

  5. 用Java实现约瑟夫环

    约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重 ...

  6. 修改eclipse启动时eclipse使用的jre

    eclipse在启动的时候,和环境变量中的jdk不兼容,可以单独制定eclipse运行的jre. 方法: 在eclipse的配置文件里增加-vm参数即可. 打开eclipse目录下的eclipse.i ...

  7. PL-SQL 存储函数和存储过程

     PL-SQL 存储函数和存储过程 ORACLE 提供能够把PL/SQL 程序存储在数据库中,并能够在不论什么地方来执行它.这样就叫存储过程或函数. 过程和函数统称为PL/SQL子程序.他们是被命 ...

  8. 【汇总】iOS开发及Xcode使用中遇到的一些报错问题汇总

    这里整合下在开发过程中遇到过的一些报错问题和解决办法:(今天开始逐渐增加)   Xcode编译错误集锦:http://www.cnblogs.com/ios-wmm/p/3402261.html   ...

  9. 多条件动态LINQ 组合查询

    本文章转载:http://www.cnblogs.com/wangiqngpei557/archive/2013/02/05/2893096.html 参考:http://dotnet.9sssd.c ...

  10. MVC + EF + Bootstrap 2 权限管理系统入门级(附源码)

    MVC .EF 学习有大半年了,用的还不是很熟练,正好以做这样一个简单的权限管理系统作为学习的切入点,还是非常合适的. 开发环境: VS 2013 + Git + MVC 5 + EF 6 Code ...