无锁数据结构(Lock-Free Data Structures)
一个星期前,我写了关于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)的更多相关文章
- 泛函编程(5)-数据结构(Functional Data Structures)
编程即是编制对数据进行运算的过程.特殊的运算必须用特定的数据结构来支持有效运算.如果没有数据结构的支持,我们就只能为每条数据申明一个内存地址了,然后使用这些地址来操作这些数据,也就是我们熟悉的申明变量 ...
- C++ 无锁数据结构
https://www.zhihu.com/question/52629893/answer/131731126
- 理解 Memory barrier(内存屏障)无锁环形队列
原文:https://www.cnblogs.com/my_life/articles/5220172.html Memory barrier 简介 程序在运行时内存实际的访问顺序和程序代码编写的访问 ...
- 使用C++11实现无锁stack(lock-free stack)
前几篇文章,我们讨论了如何使用mutex保护数据及使用使用condition variable在多线程中进行同步.然而,使用mutex将会导致一下问题: 等待互斥锁会消耗宝贵的时间 — 有时候是很多时 ...
- boost 无锁队列
一哥们翻译的boost的无锁队列的官方文档 原文地址:http://blog.csdn.net/great3779/article/details/8765103 Boost_1_53_0终于迎来了久 ...
- 并发编程(三): 使用C++11实现无锁stack(lock-free stack)
前几篇文章,我们讨论了如何使用mutex保护数据及使用使用condition variable在多线程中进行同步.然而,使用mutex将会导致一下问题: 等待互斥锁会消耗宝贵的时间 - 有时候是很多时 ...
- 并发编程入门(三): 使用C++11实现无锁stack(lock-free stack)
前几篇文章,我们讨论了如何使用mutex保护数据及使用使用condition variable在多线程中进行同步.然而,使用mutex将会导致一下问题: 等待互斥锁会消耗宝贵的时间 - 有时候是很多时 ...
- 【DPDK】【ring】从DPDK的ring来看无锁队列的实现
[前言] 队列是众多数据结构中最常见的一种之一.曾经有人和我说过这么一句话,叫做“程序等于数据结构+算法”.因此在设计模块.写代码时,队列常常作为一个很常见的结构出现在模块设计中.DPDK不仅是一个加 ...
- 无锁同步-JAVA之Volatile、Atomic和CAS
1.概要 本文是无锁同步系列文章的第二篇,主要探讨JAVA中的原子操作,以及如何进行无锁同步. 关于JAVA中的原子操作,我们很容易想到的是Volatile变量.java.util.concurren ...
随机推荐
- 测试markdown发布
- 【Vegas原创】RHEL6.2安装vmtools
1,mount虚拟cdrom # mount /dev/cdrom /mnt/cdrom 2, ls一下,看有没有 # cd /mnt/cdrom # ls –l ...
- IntelliJ IDEA + Maven环境编写第一个hadoop程序
1. 新建IntelliJ下的maven项目 点击File->New->Project,在弹出的对话框中选择Maven,JDK选择你自己安装的版本,点击Next 2. 填写Maven的Gr ...
- 使用 DJ Java Decompiler 将整个jar包反编译成源文件
使用 DJ Java Decompiler 将整个jar包反编译成源文件 所使用的软件是 DJ Java Decompiler 3.9. 下面是一个有用的参考文档,说明如何批量编译 http://ww ...
- DRUPAL 慢的原因
不止一次听人抱怨DRUPAL 慢,在本地开发环境尤为常见,较为常见的原因有:- 本地环境造成慢的原因,最常见的是由update manager 造成的,如果你发现你开的DRUPAL 页面 一直在等待 ...
- 关于BigDecimal的使用
为什么使用BigDecimal 使用BigDecimal首先要注意到float,double是无法支持商业计算的.只能支持工程计算.即误差允许的计算.通常float占用4个字节,32位.double占 ...
- 编写并发程序 Inversion
做完了 scala parallel 课程作业后,觉得 scala 写并发程序的便捷性是 java 永远都追不上的.scala 的Future 和 Promise,java 里 Future 和 Co ...
- PHP 常见语法 集合
1.die()与exit()的真正区别 die 为 exit 的别名, 执行过程 将释放内存,停止代码执行 echo "begin exec <br/>"; show( ...
- Java版的Quartz表达式生成器,同时适用于Quartz.net(免费下载)
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用.Quartz可以用来创建简单或为运行十个,百个, ...
- js最新手机号码、身份证正则表达式
身份证正则: //身份证正则表达式(15位) isIDCard1=/^[-]\d{}((\d)|([-]))(([||]\d)|[-])\d{}$/; //身份证正则表达式(18位) isIDCard ...