预备知识

CAS机制

1. 是什么

参考附录3

CAS 是项乐观锁技术,当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。

如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前值。)

CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”这其实和乐观锁的冲突检查 + 数据更新的原理是一样的。

扩展:

Q: 悲观锁和乐观锁的主要区别.

A:

乐观锁每次获取数据的时候,都不会担心数据被修改,所以每次获取数据的时候都不会进行加锁,但是在更新数据的时候需要判断该数据是否被别人修改过。

悲观锁在读取数据也会加锁,甚至只能有1个线程可以读取数据。

2. 图解CAS

讲解参考附录1的图解.

参考i++如何在并发情况下产生问题的

package main

var i int32 = 1

func main() {
i++
}
  • 反汇编查看第6行的汇编代码,可以发现和上图中C代码类似,也是对应3个底层汇编指令。(注意单个汇编指令不一定是原子操作,只是我们分析只考虑汇编指令之间进行中断和切换)
$ go tool compile -S main.go |grep main.go:6
0x0012 00018 (main.go:6) PCDATA $2, $0
0x0012 00018 (main.go:6) PCDATA $0, $0
0x0012 00018 (main.go:6) MOVL "".i(SB), AX // 根据i的内存地址(addr1),加载i值到寄存器
0x0018 00024 (main.go:6) INCL AX // 寄存器执行自增1操作
0x0019 00025 (main.go:6) MOVL AX, "".i(SB) // 寄存器的值更新给i对应的内存地址(addr1)
  • 关键问题:

    Q: 什么叫冲突

    A: 内存实际值和携程预期值不等就是冲突。 就是携程发现内存实际值(11)!=old值(10)就是冲突,本来该携程是要对10进行加1操作的.

3. Go中的CAS操作代码示例

讲解参考附录2的代码.

// CompareAndSwapInt64 executes the compare-and-swap operation for an int64 value.
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)

预备知识点: atomic.LoadInt64和atomic.StoreInt64,是一对原子操作,分别是读取和写入一个int。

TODO: 目前不清楚附录2中提到的错误写法有啥问题。

  • 扩展

    Q: 这里引出一个新的问题,既然CAS包含了Compare和Swap两个操作,它又如何保证原子性呢?或者问CompareAndSwapInt64函数为啥能保证原子性呢? 或者问atomic包中的函数怎么保证的原子性?

    A: CAS是由CPU支持的原子操作,其原子性是在硬件层面进行保证的。至于硬件层面是怎么保证的参考该博客的后半部分

4. cas机制的缺点

  1. 高并发情况下自旋消耗cpu.(也就是compare老是失败)
  2. 只支持一个变量的原子操作,不支持多个变量(代码块)的原子操作. 代码块的原子操作,用sync.Mutex。
  3. CAS有一种特殊情况:ABA。ABA是否会导致问题需要具体来分析: (参考附录4)

    3.1 没有问题的case: 对i加一减一,导致Compare成功,其实是不会影响运行结果的。

    3.2 有问题的case: Real-world examples for ABA in multithreading

位操作和Go特殊语法iota

  • &(与操作)

    1&1=1,1&0=1,0&0=1
func main() {
var state int32
mutexLocked := int32(1)
mutexWoken := int32(2)
// & 与运算,只有两者都是1结果才是1.
fmt.Println(state&mutexLocked, state&mutexWoken)
state = mutexLocked
fmt.Println(state&mutexLocked, state&mutexWoken) // ...001 & ...001 = ...001
state = mutexWoken
fmt.Println(state&mutexLocked, state&mutexWoken) // ...010 & ...010 = ...010
}
//output
0 0
1 0
0 2
  • 左移操作 a<<n: a左移n位
1<<0 = 1 = 1
1<<1 = 10 = 2
1<<2 = 100 = 4
  • Golang特殊语法:iota

    参考附录5
const (
mutexLocked = 1 << iota // mutex is locked // 1左移iota(0)位赋值给变量 // iota = 0
mutexWoken // 1左移iota(1)位赋值给变量 // iota = 1
mutexStarving // 1左移iota(2)位赋值给变量 // iota = 2
mutexWaiterShift = iota // iota = 3
) func main() {
fmt.Println(mutexLocked, mutexWoken, mutexStarving, mutexWaiterShift)
}
//output
1 2 4 3
  • 按位清除(&^)
  1. 执行规则: a &^ b == a & (^b),即与上非b。
  2. 语句用途: 把a中某些位置清空(置0),某些位置是指b中为1的位置。

    一般是 待清空的数a &^ 标志位为1的等长数字b == 执行后,标志位被清空,其他数字不变。
package main
import (
"fmt"
)
func main() {
// new &^= mutexWoken,就是清除mutexWoken位,置为 0
fmt.Println(1 &^ 1)
fmt.Println(0 &^ 1)
fmt.Printf("%b\n",1 & ^1)
fmt.Printf("%b\n",0 & ^1)
}
//output : 从执行结果看,
0
0
0
0
  • 设置,清空和读取1个标志位
package main
import (
"fmt"
)
// 1.3 与 1.7 老的实现共用的常量
const (
mutexLocked = 1 << iota // mutex is locked
mutexWoken
)
var state int32
func main() {
fmt.Printf("%b,%b,%b\n",state,mutexLocked,mutexWoken)
// 1. "|"写入标志位(置1)
wokenState := state | mutexWoken
wokenState |= mutexLocked
fmt.Printf("%b,%08b\n",wokenState,wokenState) // %08b: 输出8位,位数不足填充0
fmt.Printf("%b,%b\n",wokenState & mutexLocked,wokenState & mutexWoken) // 3.读取标志位. 如果标志位是1,读取结果就是mutexLocked或mutexWoken
// 2. "&^"清空标志位,即置0
wokenState &^= mutexWoken
fmt.Printf("%08b\n",wokenState)
}
//output
0,1,10
11,00000011
1,10
00000001

raceenabled是什么代码

参考附录11.

raceenabled相关的代码全部忽略,这是golang内部使用thread-sanitizer用于扫描线程安全问题的诊断代码。

参考资料

1.图解CAS

2.GO中CAS代码示例

3.CAS乐观锁,最精确的文字描述.

4.CAS会导致"ABA问题"

5.Go-iota使用例子

6.sync.Mutex的实现和演进

7.Go语言中你所不知道的位操作用法 TODO: 通过位操作实现用户类型的存储,更新和修改。如[一个qq号可以用VIP会员,SVIP超级会员,蓝钻用户,黄钻用户,红钻用户....]

8.位清除(&^)有哪些应用?

9.go语言中获取变量类型的三种方法

10.Golang中的int类型和uint类型到底有多大? TODO: int和uint是根据 CPU 变化的!!!

11.毛剑-1.3sync.Mutex源码解析

Go中锁的那些姿势,估计你不知道

【协作式原创】查漏补缺之Golang中mutex源码实现(预备知识)的更多相关文章

  1. 【协作式原创】查漏补缺之Golang中mutex源码实现

    概览最简单版的mutex(go1.3版本) 预备知识 主要结构体 type Mutex struct { state int32 // 指代mutex锁当前的状态 sema uint32 // 信号量 ...

  2. 半夜思考之查漏补缺, 在 Spring中, 所有的 bean 都是 Spring 创建的吗 ?

    Spring 是一个 bean 容器, 负责 bean 的创建, 那么所有的 bean对象都是 Spring 容器创建的吗 ? 答案是否定的. 但是乍一想, 好像所有的对象都是 Spring 容器负责 ...

  3. 查漏补缺:Vector中去重

    对于STL去重,可以使用<algorithm>中提供的unique()函数. unique()函数用于去除相邻元素中的重复元素(所以去重前需要对vector进行排序),只留下一个.返回去重 ...

  4. Java查漏补缺(3)(面向对象相关)

    Java查漏补缺(3) 继承·抽象类·接口·静态·权限 相关 this与super关键字 this的作用: 调用成员变量(可以用来区分局部变量和成员变量) 调用本类其他成员方法 调用构造方法(需要在方 ...

  5. Java基础查漏补缺(2)

    Java基础查漏补缺(2) apache和spring都提供了BeanUtils的深度拷贝工具包 +=具有隐形的强制转换 object类的equals()方法容易抛出空指针异常 String a=nu ...

  6. CSS基础面试题,快来查漏补缺

    本文大部分问题来源:50道CSS基础面试题(附答案),外加一些面经. 我对问题进行了分类整理,并给了自己的回答.大部分知识点都有专题链接(来源于本博客相关文章),用于自己前端CSS部分的查漏补缺.虽作 ...

  7. Go语言知识查漏补缺|基本数据类型

    前言 学习Go半年之后,我决定重新开始阅读<The Go Programing Language>,对书中涉及重点进行全面讲解,这是Go语言知识查漏补缺系列的文章第二篇,前一篇文章则对应书 ...

  8. 《CSS权威指南》基础复习+查漏补缺

    前几天被朋友问到几个CSS问题,讲道理么,接触CSS是从大一开始的,也算有3年半了,总是觉得自己对css算是熟悉的了.然而还是被几个问题弄的"一脸懵逼"... 然后又是刚入职新公司 ...

  9. js基础查漏补缺(更新)

    js基础查漏补缺: 1. NaN != NaN: 复制数组可以用slice: 数组的sort.reverse等方法都会改变自身: Map是一组键值对的结构,Set是key的集合: Array.Map. ...

随机推荐

  1. configure: error: no acceptable C compiler found in $PATH 解决

    在安装keepalived时报错 ./configure --prefix=/usr/local/ccbase/keepalived-2.0.15 && make && ...

  2. [蓝桥杯][基础训练]FJ的字符串

    Description FJ在沙盘上写了这样一些字符串: A1 = “A” A2 = “ABA” A3 = “ABACABA” A4 = “ABACABADABACABA” … … 你能找出其中的规律 ...

  3. 思科ISE配置专题–ISE部署方式

    ISE部署方式有三种: 1.Standalong Deployment 所谓Standalong部署就是只有一台ISE,所有的组件都安装在这一台上面.一台ISE装好的时候默认是“Standalong” ...

  4. Codeforces 1315C Restoring Permutation

    You are given a sequence b1,b2,…,bnb1,b2,…,bn . Find the lexicographically minimal permutation a1,a2 ...

  5. bodyParser.urlencoded({ })里extended: true和false区别???

  6. python浅析对return的理解

    函数外部的代码要想获取函数的执行结果,就可以在函数里面用return语句,把结果返回. return 代表一个函数的终止,如果return 后面带一个print 或者return  ,则后面的不执行 ...

  7. Tika结合Tesseract-OCR 实现光学汉字识别(简体、宋体的识别率百分之百)—附Java源码、测试数据和训练集下载地址

     OCR(Optical character recognition) —— 光学字符识别,是图像处理的一个重要分支,中文的识别具有一定挑战性,特别是手写体和草书的识别,是重要和热门的科学研究方向.可 ...

  8. Java连载82-Set、Collection、List、Map的UML演示

    一.UML演示Collection集合的继承结构图 二.Set集合 1.List存储元素的特点:有序可重复.有序,存进去是什么顺序,拿出来还是什么顺序. 2.Set存储元素的特点:无序不可重复,存进去 ...

  9. django 项目发布(centos 6.5 + python 3.5 + django1.9.8 + paramiko 2.0.2 + gunicorn )

    环境 os centos 6.5 64bit python 3.5 django 1.9.8 paramiko 2.0.2 gunicorn 19.6.0 安装 centos install pyth ...

  10. 如何利用wx.request进行post请求

    1,method 是  get  方式的时候,会将数据转换成 query string method 为 post 时,header为{"Content-Type": " ...