预备知识

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. Linux常用命令英文全称与中文解释 (pwd、su、df、du等)

    https://blog.csdn.net/qq_40334837/article/details/83819735 Linux常用命令英文全称与中文解释 apt: Advanced Packagin ...

  2. 题解【POJ2955】Brackets

    Description We give the following inductive definition of a "regular brackets" sequence: t ...

  3. python基础 pyc

    关于pyc的几点记录: python运行时会将python语句----->"字节码"------>转发到"虚拟机" 字节码:在大型的python程序 ...

  4. 每天进步一点点------altium designer Summer09出现的问题解决方案

    在编译原理图时,引脚和连线旁边出现很多红线,提示 error:signal with no driver. 原理图没有加入到Project里. 第一次导入没问题,但是改了个元件的封装,在更新一下(De ...

  5. yii2的防御csrf攻击机制

    csrf,中文名称:跨站请求伪造,可以在百度上搜索资料,详细了解这一方面的概念.对于我们是非常有帮助的.yii2的csrf的实现功能是在yii\web\request类实现功能的.request类中的 ...

  6. winform datagridview 同步滚动

    //首先添加 Scroll事件//同步滚动 private void dgYY_Scroll(object sender, ScrollEventArgs e) { ) { dgFee.FirstDi ...

  7. sshpass远程登陆

    1,ssh ssh 端口为默认22的时候: sshpass -p 888888 scp -o StrictHostKeyChecking=no /root/images.zip root@21.1.9 ...

  8. reStructuredText语法

    reStructuredText 除了makedown语法这还存在另一种语法reStructuredText 相对Markdown来说,在写书方面更有优势: 使用sphnix能够自动生成目录和索引文件 ...

  9. 你了解真正的 restful API 吗?

    本文原创地址,博客:https://jsbintask.cn/2019/03/20/api/restful-api-best-practices/(食用效果最佳),转载请注明出处! 前言 在以前,一个 ...

  10. windows centos php-beast 安装

    https://github.com/imaben/php-beast-binaries windows下 可以直接在这里下载dll 根据自己的php版本  还有是不是线程安全的 来选择下载对应的 放 ...