预备知识

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. Mongodb学习笔记(二)Capped Collection固定集合

    一.Capped Collection固定集合 简单介绍 capped collections是性能出色的有着固定大小的集合(定容集合),以LRU(Least Recently Used最近最少使用) ...

  2. Android学习15

    Date&Time DatePicker(日期选择器),TimePicker(时间选择器),CalendarView(日期视图): 1.TextClock TextClock可以以字符串格式显 ...

  3. 作业1:使用go搭建一个web-server

    todo1:搭建web-server的原理 todo2:go实现web-server

  4. eclipse的版本代号

    mars为4.5版本号 代号 代号名 发布日期Eclipse 3.1 IO 木卫一,伊奥 2005Eclipse 3.2 Callisto 木卫四,卡里斯托 2006Eclipse 3.3 Europ ...

  5. redis-server.exe redis.windows.conf 报错

    在参考博文:https://blog.csdn.net/erlian1992/article/details/54382443#comments 学习redis的时候启动报错 C:\Users\Adm ...

  6. queue的使用-Hdu 1702

    ACboy needs your help again! Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K ( ...

  7. 一文搞懂vim复制粘贴

    转载自本人独立博客https://liushiming.cn/2020/01/18/copy-and-paste-in-vim/ 概述 复制粘贴是文本编辑最常用的功能,但是在vim中复制粘贴还是有点麻 ...

  8. Django - 在settings配置终端打印SQL语句

    LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console': { 'level': 'DE ...

  9. 清空表单 autocomplete="off"

    清空表单 autocomplete="off" <form action="/sm/baziqiming.aspx" method="post& ...

  10. 安卓开发:Android Studio自动import

    我只想说,真好用!哈哈,提高效率的好东西. 参考: [https://blog.csdn.net/pjdd123/article/details/80953669] [https://www.cnbl ...