一文读懂原子操作、内存屏障、锁(偏向锁、轻量级锁、重量级锁、自旋锁)、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 释义:批大小, ...
随机推荐
- Excel批量转Word
平时的工作中,尤其是一些文职类工作中,常会遇到这样的问题: 我们有一个Excel文件,每一行都是一个人的信息,需要将每个人的信息填写到Word表格中:一个人,一张表,一个下午,真烦恼. 也是奇想突发, ...
- ruoyi首次使用常见问题的解决方案
1.导入项目之后,下载依赖包之后,模块的依赖项飘红(我这里无法复现,当参考图吧) 解决方法: 2.ruoyi框架代码生成之后,需要自己进行替换到指定位置.相应的官方文档位置,否则,可能会出现404,访 ...
- Hashmap如何同步?
当我们需要一个同步的HashMap时,有两种选择: ●使用Collections.synchronizedMap(..)来同步HashMap. ●使用ConcurrentHashMap的 这两个选项之 ...
- keybd_event 在F按键系列不起作用的解决办法
最近给公司做自动化测试工具,主要用到的功能是模拟鼠标键盘录制回放.一切都很完美了,但在客户大机系统上使用的时候,发现F1-F24系列按键无法正确使用,查了很多资料,主要有2个方面的原因: 1.一些游戏 ...
- IDEA 创建javaWeb以及Servlet
1.新建项目 2.Web工程设置:点击项目名称,按F4 (1)配置sources:在WEB-INF下新建两个文件夹classes和lib (2)配置path:刚刚创建的classes文件夹路径 (3) ...
- Linux用户无法使用sudo命令
新建的用无法使用sudo命令,出现这样的提示: xiaojing is not in the sudoers file. This incident will be reported 原来是新建的用户 ...
- SpringMVC的入门程序
1.环境准备(jar包) 2.在web.xml中配置前端控制器 <!-- springmvc 前端控制器 --> <servlet> <servlet-name>s ...
- 03. Pandas数据结构
03. Pandas数据结构 Series DataFrame 从DataFrame中查询出Series 1. Series Series是一种类似于一维数组的对象,它由一组数据(不同数据类型)以及一 ...
- anijs 一个小巧的动画库
很多时候我意识到前端已近变成写h5宣传页面 我不知道是可悲 还是生活的必然 小问题 使用css animation和js animation api制作动画是目前比较流行的做法 但是最后很多人的代码就 ...
- 实用的 CSS — 贝塞尔曲线(cubic-bezier)
欢迎移步我的博客阅读:<实用的 CSS - 贝塞尔曲线(cubic-bezier)> 前言 在了解 cubic-bezier 之前,你需要对 CSS3 中的动画效果有所认识,它是 anim ...