一文读懂原子操作、内存屏障、锁(偏向锁、轻量级锁、重量级锁、自旋锁)、Disruptor、Go Context之上半部分

本文从并发与并行的概念出发,深入探讨了硬件底层原因,如多核CPU的并发冲突和伪共享问题,以及解决这些问题的CPU缓存结构和MESI协议。接着介绍了原子操作,如CAS,以及内存屏障的作用,防止编译器优化导致的顺序问题。文章还讨论了Java中的偏向锁、轻量级锁、重量级锁和自旋锁,解释了锁的升级机制。最后提到了Go中的Context和atomic.Value在并发控制中的应用。
摘要由CSDN通过智能技术生成

🚀 优质资源分享 🚀

学习路线指引(点击解锁) 知识定位 人群定位
🧡 Python实战微信订餐小程序 🧡 进阶级 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
💛Python量化交易实战💛 入门级 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统

我不想卷,我是被逼的

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

有句话叫做初出茅庐天下无敌,再练三年寸步难行。本着不服输精神回来研究了一下这个问题,很简单需要使用Go提供的Context,api使用起来也很简单,但是我一向喜欢刨根问底,于是乎研究Context源码发现互斥锁(Mutex)、原子操作(atomic),研究atomic发现CAS,研究CAS发现了java的自旋锁、偏向锁、轻量级锁、重量级锁,研究锁发现Disruptor,研究Disruptor发现CPU伪共享、MESI协议、内存屏障。

0

所以这篇文章会自下向上的讲解,先讲CPU硬件设计带来的优势以及带来的问题(多核并发冲突、伪共享),从底层上理解并发问题存在的根因,然后讲解原子操作与CAS,之后讲解操作系统为解决并发问题的锁机制、信号量,然后介绍下高并发框架Disruptor利用这些机制的高性能实现,最后回到Go中的atomic.Value以及建立在aotmic.Value和Mutex之上的Context,最后的最后回答下那位老哥问题,怎么使用Context来做goroutine的调度。

并发与并行

并行是并发的一个子集;并发(ConcurrentMode)强调的是从任务调度角度来看,同时安排多个是任务;任务可以穿插着执行,并不一定是同一时刻在同时进行;并行(Parallel)是从实际执行角度来看真的有多个任务在同一时刻同时执行。
现代CPU多半是多核设计,所以我理解会存在以多核并行的方式进行高并发运行。当然单核单线程依然存在并行,它上面也存在真正的“并行”。只不过,这个并行并不是CPU内部的、线程之间的并行;而是CPU执行程序的同时,DMA控制器也在执行着网络报文收发、磁盘读写、音视频播放/录制等等任务。
不像js这种单线程异步的语言,向java、Go语法上看起来都是以多线程同步阻塞的形式运行,一般多个请求来时,都是开启一个线程池利用多线程的方式进行处理。得益于CPU多核的优势,可以快速并行运行请求任务,但是同样因为这个原因会天然的引起多线程访问数据冲突。

硬件底层原因

并发冲突
并发资源冲突的底层原因与CPU设计有关,先看CPU的缓存结构
0
0
可以看到i7-8700k是6核,右下角L1、L2都6x多少kb,可以看到L1、L2缓存是各个核心独有的,L3是共用的,读取数据时会先从内存中把数据和指令读到自己的L1缓存中,这就可以知道当两个线程访问同一个数据时,CPU角度他们其实在各自核心中都是独有的。
同样在写的时候,CPU为了节省跟内存通信带来的性能开销,也并不一定是程序更新后立即放到内存中。而是有两种策略:写直达和写回。写直达比较简单,都是每次写入到内存中,性能开销大。而写回是当L1数据缓存中的变量A被更改后不立即写回内存,只是做一个脏标记,这样多次写同一个变量A可以一直在L1中处理,直到这个缓存位置的A不在处理了,需要A腾出地方给另一个变量B时,才把A的数据写到内存中,这样提升缓存命中率,减少与内存的通信提升性能。
可以想象的是,这种高性能在多核并发运行时,两个核心都读到了变量A值为0,核心1上运行的线程T1把A改成了10,但没有写到内存中,也没有通知核心2,它的L1缓存中A还是0,这时候核心2的线程T2把A改成了20;当他们都往内存写的时候,必然出现冲突。这就是在单机上多线程并发引起的资源冲突的底层原因,也是后续各种原子操作、锁、信号量等机制要解决的问题。推广到宏观业务场景是一样的,两个服务为了性能优化,先把数,0读到自己的内存中,一个改成了1,一个改成了2,而数据库看到的还是0,这时候两个服务都往数据库写,就会产生冲突。

原子操作
为了解决这个问题,早期科学家走了很多弯路,花了好多年才找到解决软件和硬件的解决方案。首先在CPU硬件层面,有两个手段:写传播和MESI协议。
写传播的方案就

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值