一文读懂原子操作、内存屏障、锁(偏向锁、轻量级锁、重量级锁、自旋锁)、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 释义:批大小, ...
随机推荐
- Spring cache源码分析
Spring cache是一个缓存API层,封装了对多种缓存的通用操作,可以借助注解方便地为程序添加缓存功能. 常见的注解有@Cacheable.@CachePut.@CacheEvict,有没有想过 ...
- grep 命令?
强大的文本搜索命令,grep(Global Regular Expression Print) 全局正则表达式搜索.grep 的工作方式是这样的,它在一个或多个文件中搜索字符串模板.如果模板包括空格, ...
- SpringCloud和Dubbo?
SpringCloud和Dubbo都是现在主流的微服务架构SpringCloud是Apache旗下的Spring体系下的微服务解决方案Dubbo是阿里系的分布式服务治理框架从技术维度上,其实Sprin ...
- Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?
Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询.在 Myba ...
- 什么是springboot?为什么要用springboot?
一.什么是springboot? Springboot是spring发展到一定程度的产物,但并不是spring的替代品,springboot是为了让程序员更好的使用spring.Spring随着发展逐 ...
- windows环境Jenkins配置与使用(springboot+war包+vue)
一.后台发布 1.General配置 2.源码管理 3.构建触发器 4.构建环境 5.构建 clean install -Dmaven.test.skip=true -Ptest 6.Post Ste ...
- mysql问题排查与性能优化
MySQL 问题排查都有哪些手段? 使用 show processlist 命令查看当前所有连接信息. 使用 explain 命令查询 SQL 语句执行计划. 开启慢查询日志,查看慢查询的 SQL. ...
- 关于XML文件
关于xml文件没有提示(eclipse) 点我
- sudo rosdep init 出现 ERROR: cannot download default sources list from:错误解决方法
关于安装ROS时出现的rosdep init错误 sudo rosdep init ERROR: cannot download default sources list from: https:// ...
- Numpy中重要的广播概念
Numpy中重要的广播概念 广播:简单理解为用于不同大小数组的二元通用函数(加.减.乘等)的一组规则 广播的规则: 如果两个数组的维度数dim不相同,那么小维度数组的形状将会在左边补1 如果shape ...