一个星期前,我写了关于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. Linux录屏软件

    如何查找录屏软件 apt-cache search screen record libutempter-dev - privileged helper for utmp/wtmp updates (d ...

  2. [转]揭秘webdriver实现原理

    转自:http://www.cnblogs.com/timsheng/archive/2012/06/12/2546957.html 通过研究selenium-webdriver的源码,笔者发现其实w ...

  3. 1、Orchard商城开发——开发需求

    需要开发的功能: 1.商品详情,可添加商品属性,如颜色,尺寸等. 2.商品类别,可显示该类别下的所有商品,可按品牌.颜色.尺寸等检索,并可按价格.销量等排序游览. 3.商品游览记录,收藏商品,加入购物 ...

  4. C语言代写

    MTRX1702 - C ProgrammingAssignment 2This assignment requires you to design and build a program that ...

  5. Java 中类的加载顺序

    如果类A和类B中有静态变量,静态语句块,非静态变量,非静态语句块,构造函数,静态方法,非静态方法,同时类A继承类B,请问当实例化A时,类内部的加载顺序是什么? 测试代码如下: Class B: pub ...

  6. struts2:遍历自定义字符串数组,遍历Action实例所引用对象中的数组

    在struts2:OGNL表达式,遍历List.Map集合:投影的使用一文中已经讲述了OGNL遍历List.Map集合等功能. 本文简单写一个遍历数组的示范程序. 1. 遍历自定义字符串数组 < ...

  7. Swift 关键字汇总

    常见的关键字有以下4种 与声明有关的关键字:class.deinit.enum.extension.func.import.init.let.protocol.static.struct.subscr ...

  8. CISA 信息系统审计知识点 [第二章. IT治理和管理 ]

    第二章.  IT治理和管理 1.  IT治理.管理.安全和控制框架及标准.指南和实践 IT治理是董事会和执行管理层的职责. IT治理的关键因素:保持与业务的战略一致,引导业务价值的实现. IT治理关注 ...

  9. 物料分类账 [COML] PART 1 - 概览

    物料分类账 [COML] PART 1 - 概览 一).原理 1). •实际成本/物料分类帐是产品成本控制模块的一个子模块,产品成本控制包括三个子模块,产品成本计划,成本对象控制,实际成本/物料分类帐 ...

  10. PL/SQL Developer去掉启动时自动弹出的Logon弹出框方法

    以前用PL/SQL Developer 7.0版本,最近升级到PL/SQL Developer 11.0版本,但每次启动PL/SQL Developer都会自动弹出Logon窗口,并且选中其中的登录历 ...