一文读懂原子操作、内存屏障、锁(偏向锁、轻量级锁、重量级锁、自旋锁)、Disruptor、Go Context之上半部分
我不想卷,我是被逼的
在做了几年前端之后,发现互联网行情比想象的差,不如赶紧学点后端知识,被裁之后也可接个私活不至于饿死。学习两周Go,如盲人摸象般不知重点,那么重点谁知道呢?肯定是使用Go的后端工程师,那便利用业余时间找了几个老哥对练一下。其中一位问道在利用多个goroutine发送请求拿到结果之后如果进行销毁。是个好问题,研究了一下需要利用Context,而我一向喜欢研究源码,继续深挖发现细节非常多,于是乎有此这篇文章。

并发与并行
硬件底层原因



- Modified,已修改
- Exclusive,独占
- Shared,共享
- Invalidated,已失效


原子操作与内存屏障

type Value struct { v interface{} }
type ifaceWords struct {
typ unsafe.Pointer
data unsafe.Pointer
}
bytes := []byte{104, 101, 108, 108, 111}
p := unsafe.Pointer(&bytes) //强制转换成unsafe.Pointer,编译器不会报错
str := *(*string)(p) //然后强制转换成string类型的指针,再将这个指针的值当做string类型取出来
fmt.Println(str) //输出 "hello"
func (v *Value) Store(x interface{}) {
if x == nil {
panic("sync/atomic: store of nil value into Value")
}
// 通过unsafe.Pointer将现有的和要写入的值分别转成ifaceWords类型,
// 这样我们下一步就可以得到这两个interface{}的原始类型(typ)和真正的值(data)
vp := (*ifaceWords)(unsafe.Pointer(v)) // Old value
xp := (*ifaceWords)(unsafe.Pointer(&x)) // New value
// 这里开始利用CAS来自旋了
for {
// 通过LoadPointer这个原子操作拿到当前Value中存储的类型
typ := LoadPointer(&vp.typ)
if typ == nil {
// typ为nil代表Value实例被初始化,还没有被写入数据,则进行初始写入;
// 初始写入需要确定typ和data两个值,非初始写入只需要更改data
// Attempt to start first store.
// Disable preemption so that other goroutines can use
// active spin wait to wait for completion; and so that
// GC does not see the fake type accidentally.
// 获取runtime总当前P(调度器)并设置禁止抢占,使得goroutine执行当前逻辑不被打断以便尽快完成,同时这时候也不会发生GC
// pin函数会将当前 goroutine绑定的P, 禁止抢占(preemption) 并从 poolLocal 池中返回 P 对应的 poolLocal
runtime_procPin()
// 使用CAS操作,先尝试将typ设置为^uintptr(0)这个中间状态。
// 如果失败,则证明已经有别的线程抢先完成了赋值操作,那它就解除抢占锁,然后重新回到 for 循环第一步进行自旋
// 回到第一步后,则进入到if uintptr(typ) == ^uintptr(0)这个逻辑判断和后面的设置StorePointer(&vp.data, xp.data)
if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) {
// 设置成功则将P恢复原样
runtime_procUnpin()
continue
}
// Complete first store.
// 这里先写data字段在写typ字段,因为这个两个单独都是原子的
// 但是两个原子放在一起未必是原子操作,所以先写data字段,typ用来做判断
StorePointer(&vp.data, xp.data)
StorePointer(&vp.typ, xp.typ)
runtime_procUnpin()
return
}
if uintptr(typ) == ^uintptr(0) {
// 这个时候typ不为nil,但可能为^uintptr(0),代表当前有一个goroutine正在写入,还没写完
// 我们先不做处理,保证那个写入线程操作的原子性
// First store in progress. Wait.
// Since we disable preemption around the first store,
// we can wait with active spinning.
continue
}
// First store completed. Check type and overwrite data.
if typ != xp.typ { // atomic.Value第一确定类型之后,后续都不能改变
panic("sync/atomic: store of inconsistently typed value into Value")
}
// 非第一次写入,则利用StorePointer这个原子操作直接写入。
StorePointer(&vp.data, xp.data)
return
}
}
func (v *Value) Load() (x interface{}) {
vp := (*ifaceWords)(unsafe.Pointer(v))
typ := LoadPointer(&vp.typ) // 原子性读
// 如果当前的typ是 nil 或者^uintptr(0),那就证明第一次写入还没有开始,或者还没完成,那就直接返回 nil (不对外暴露中间状态)。
if typ == nil || uintptr(typ) == ^uintptr(0) {
// First store not yet completed.
return nil
}
// 否则,根据当前看到的typ和data构造出一个新的interface{}返回出去
data := LoadPointer(&vp.data)
xp := (*ifaceWords)(unsafe.Pointer(&x))
xp.typ = typ
xp.data = data
return
}

偏向锁、轻量级锁、重量级锁、自旋锁



/**@param delta the value to add
* @return the previous value
*/
* Atomically adds the given value to the current value.
*
*
public final int getAndAdd(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return current;
}
} /**@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
* Atomically sets the value to the given updated value
* if the current value {
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
参考资料
- 深入理解Go Context:https://article.itxueyuan.com/39dbvb
- context源码:https://github.com/golang/go/blob/master/src/context/context.go
- 聊一聊Go的Context上下文:https://studygolang.com/articles/28726
- go context详解:https://www.cnblogs.com/juanmaofeifei/p/14439957.html
- Go语言Context(上下文):http://c.biancheng.net/view/5714.html
- atomic原理以及实现:https://blog.csdn.net/u010853261/article/details/103996679
- atomic前世今生:https://blog.betacat.io/post/golang-atomic-value-exploration/
- CAS乐观锁:https://blog.csdn.net/yanluandai1985/article/details/82686486
- CAS乐观锁:https://blog.csdn.net/nrsc272420199/article/details/105032873
- 偏向锁、轻量级锁、重量级锁、自旋锁原理:https://blog.csdn.net/qq_43141726/article/details/118581304
- 自旋锁,偏向锁,轻量级锁,重量级锁:https://www.jianshu.com/p/27290e67e4d0
- CAS与自旋锁:https://blog.csdn.net/weixin_52904390/article/details/113700649
- 自旋锁、CAS、悲观锁、乐观锁:https://blog.csdn.net/weixin_45102619/article/details/120605691
- Go并发面试总结:https://www.iamshuaidi.com/8942.html
- 高性能队列-Disruptor:https://tech.meituan.com/2016/11/18/disruptor.html
- 锁与原子操作的关系:https://www.cnblogs.com/luconsole/p/4944304.html
- 多线程顺序打印:https://www.cnblogs.com/lazyegg/p/13900847.html
- 如何实现一个乐观锁:https://zhuanlan.zhihu.com/p/137818729
- disruptor与内存屏障:http://ifeve.com/disruptor-memory-barrier/
- Java volatile的作用:http://www.51gjie.com/java/574.html
- 浅谈原子操作:https://zhuanlan.zhihu.com/p/333675803
- sync.Pool设计思路:https://blog.csdn.net/u010853261/article/details/90647884
一文读懂原子操作、内存屏障、锁(偏向锁、轻量级锁、重量级锁、自旋锁)、Disruptor、Go Context之上半部分的更多相关文章
- 一文读懂数仓中的pg_stat
摘要:GaussDB(DWS)在SQL执行过程中,会记录表增删改查相关的运行时统计信息,并在事务提交或回滚后记录到共享的内存中.这些信息可以通过 "pg_stat_all_tables视图& ...
- 一文读懂HTTP/2及HTTP/3特性
摘要: 学习 HTTP/2 与 HTTP/3. 前言 HTTP/2 相比于 HTTP/1,可以说是大幅度提高了网页的性能,只需要升级到该协议就可以减少很多之前需要做的性能优化工作,当然兼容问题以及如何 ...
- 一文读懂AI简史:当年各国烧钱许下的愿,有些至今仍未实现
一文读懂AI简史:当年各国烧钱许下的愿,有些至今仍未实现 导读:近日,马云.马化腾.李彦宏等互联网大佬纷纷亮相2018世界人工智能大会,并登台演讲.关于人工智能的现状与未来,他们提出了各自的观点,也引 ...
- 一文读懂高性能网络编程中的I/O模型
1.前言 随着互联网的发展,面对海量用户高并发业务,传统的阻塞式的服务端架构模式已经无能为力.本文(和下篇<高性能网络编程(六):一文读懂高性能网络编程中的线程模型>)旨在为大家提供有用的 ...
- [转帖]一文读懂 HTTP/2
一文读懂 HTTP/2 http://support.upyun.com/hc/kb/article/1048799/ 又小拍 • 发表于:2017年05月18日 15:34:45 • 更新于:201 ...
- 即时通讯新手入门:一文读懂什么是Nginx?它能否实现IM的负载均衡?
本文引用了“蔷薇Nina”的“Nginx 相关介绍(Nginx是什么?能干嘛?)”一文部分内容,感谢作者的无私分享. 1.引言 Nginx(及其衍生产品)是目前被大量使用的服务端反向代理和负载均衡 ...
- kubernetes基础——一文读懂k8s
容器 容器与虚拟机对比图(左边为容器.右边为虚拟机) 容器技术是虚拟化技术的一种,以Docker为例,Docker利用Linux的LXC(LinuX Containers)技术.CGroup(Co ...
- 大数据篇:一文读懂@数据仓库(PPT文字版)
大数据篇:一文读懂@数据仓库 1 网络词汇总结 1.1 数据中台 数据中台是聚合和治理跨域数据,将数据抽象封装成服务,提供给前台以业务价值的逻辑概念. 数据中台是一套可持续"让企业的数据用起 ...
- 一文读懂神经网络训练中的Batch Size,Epoch,Iteration
一文读懂神经网络训练中的Batch Size,Epoch,Iteration 作为在各种神经网络训练时都无法避免的几个名词,本文将全面解析他们的含义和关系. 1. Batch Size 释义:批大小, ...
随机推荐
- CF1556F Sports Betting (状压枚举子集DP)
F 对于一张比赛图,经过缩点,会得到dag,且它一定是transitive的,因此我们能直接把比赛图缩成一个有向链.链头作为一个强连通分量,里面的所有点都是胜利的 定义F(win)表示win集合作为赢 ...
- jdk源码阅读笔记
1.环境搭建 http://www.komorebishao.com/2020/idea-java-jdk-funyard/ 2. 好的源码阅读资源 https://zhuanlan.zhihu.co ...
- Java中的list和set有什么区别
list与set方法的区别有:list可以允许重复对象和插入多个null值,而set不允许:list容器是有序的,而set容器是无序的等等 Java中的集合共包含三大类,它们分别是Set(集),Lis ...
- 面试问题之C++语言:C++中指针和引用的区别
转载于:https://blog.csdn.net/gcc2018/article/details/82285940 1.指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元:而引用 ...
- Java 中是如何支持正则表达式操作的?
Java 中的 String 类提供了支持正则表达式操作的方法,包括:matches(). replaceAll().replaceFirst().split().此外,Java 中可以用 Patte ...
- 怎样将 GB2312 编码的字符串转换为 ISO-8859-1 编码的 字符串?
String s1 = "你好"; String s2 = new String(s1.getBytes("GB2312"), "ISO-8859-1 ...
- Spring 切面可以应用五种类型的通知?
Spring 切面可以应用五种类型的通知: before:前置通知,在一个方法执行前被调用. after: 在方法执行之后调用的通知,无论方法执行是否成功. after-returning: 仅当方法 ...
- MyBatis Plus 2.3 个人笔记-02-基本注解
实体类注解 /* * MybatisPlus会默认使用实体类的类名到数据中找对应的表. * */ @TableName("tbl_employee") public class E ...
- h5 ios输入框与键盘 兼容性优化
起因 h5的输入框引起键盘导致体验不好,目前就算微信.知乎.百度等产品也没有很好的技术方案实现,尤其底部固定位置的输入框各种方案都用的前提下体验也并没有很好,这个问题也是老大难问题了.目前在准备一套与 ...
- 使用Canvas和JavaScript做一个画板
本文同步于个人博客:https://zhoushuo.me/blog/2018/03/11/drawing-borad/ 前些天学习了HTML5的<canvas>元素,今天就来实践一下,用 ...