如何将自己做的网站推广出去/惠州搜索引擎优化
Sync
Go 语言作为一个原生支持用户态进程(Goroutine)的语言,当提到并发编程、多线程编程时,往往都离不开锁这一概念。锁是一种并发编程中的同步原语(Synchronization Primitives),它能保证多个 Goroutine 在访问同一片内存时不会出现竞争条件(Race condition)等问题。
通过atomic.CompareAndSwapInt32
调用汇编CAS(compare and swap)指令的原子性来实现临界区的互斥访问,保证只有一个协程获取到锁
当其中一个 goroutine 获得了这个锁,其他 goroutine 尝试获取这个锁时将会被阻塞,直到持有锁的 goroutine 释放锁为止。
Go 语言在 sync
包中提供了用于同步的一些基本原语,包括常见的 sync.Mutex
、sync.RWMutex
、sync.WaitGroup
、sync.Once
和 sync.Cond
:
!Mutex互斥锁
Go 语言的 sync.Mutex
由两个字段 state
和 sema
组成。其中 state
表示当前互斥锁的状态,而 sema
是用于控制锁状态的信号量。
type Mutex struct {state int32sema uint32 // 指针地址 0xF,存着结构体的地址
}
Mutex.state
状态字段
int32类型的state代表:
-
locked: 锁状态 1被锁 0未被锁
-
woken:1是否有goroutine模式被唤醒,0未被唤醒
-
starving:1进入饥饿模式,0正常模式
-
其他位:代表获取锁的等待队列中的协程数,state是int32类型,说明是32bit,其余位是32-3 bits,所以最大排队协程数就是2^(32-3)
锁模式
- 正常模式:队头和新协程的抢占,未抢占到的扔到队尾
- 饥饿模式:按顺序获取锁,不得插队,防止队尾一直阻塞等待
正常模式
在正常模式下获取锁:
- 多线程下竞争锁,获取成功返回,修改
sync.Mutex
结构体字段。获取失败,自旋等待其他线程释放锁,4次之后仍然拿不到锁,goroutine加入到等待队列尾部,状态改成_GWaiting
- 获取到锁的线程释放锁,从等待队列头部唤醒一个Goroutine,状态改成
_Grunning
,他会和新创建并且获取锁的新goroutine(M正在运行的g_Grunning)争抢锁。- 如果被唤醒的G仍然未能抢到锁,goroutine加入到等待队列头部,状态改成
_GWaiting
- 如果被唤醒的G抢到锁,新创建的G相当于重新进入1步骤
- 如果被唤醒的G仍然未能抢到锁,goroutine加入到等待队列头部,状态改成
饥饿模式
在饥饿模式下获取锁:
互斥锁会直接交给等待队列最前面的 Goroutine。新的 Goroutine 在该状态下不能获取锁、也不会进入自旋状态,它们只会在队列的末尾等待。如果一个 Goroutine 获得了互斥锁并且它在队列的末尾或者它等待的时间少于 1ms,那么当前的互斥锁就会切换回正常模式。
锁模型切换
- 正常模式切换到饥饿模式:被唤醒的 Goroutine 超过 1ms 没有获取到锁,它就会将当前互斥锁切换饥饿模式,防止部分 Goroutine 被『饿死』。
- 饥饿模式换到正常模式切:
- 一个 Goroutine 获得了互斥锁并且它在队列的末尾,说明没有协程在竞争了,切换到正常模式
- 被唤醒的 Goroutine 获得锁没超过 1ms ,切换到正常模式
Mutex.Sema
控制锁状态的信号量(互斥信号量)
// runtime/sema.go
type semaRoot struct {lock mutextreap *sudog // 锁抢占者的 平衡树的根nwait uint32 // 抢占锁的goroutine的数量
}
互斥锁加锁/解锁
func (m *Mutex) Lock()
:Lock方法锁住m,如果m已经加锁,则阻塞直到m解锁。
func (m *Mutex) Lock() {// 未锁状态,获取锁returnif atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {if race.Enabled {race.Acquire(unsafe.Pointer(m))}return}// Slow path (outlined so that the fast path can be inlined)m.lockSlow()
}func (m *Mutex) lockSlow() {var waitStartTime int64 // 协程抢占锁时间,时间超出,锁变成饥饿模式starving := falseawoke := falseiter := 0old := m.statefor {// 锁住状态下 and 不是饥模式 and 在可自旋次数下 进入if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) {// awoke标记是false and 锁非唤醒状态 and 锁的等待者大于0 // 满足这些条件,把锁变成唤醒状态// awoke flag标记成trueif !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {awoke = true}// 自旋 汇编runtime_doSpin()// 累计自选次数iter++// 把唤醒状态 覆盖 oldold = m.statecontinue}// 可能其他协程更改了锁状态:改成了`未锁住状态` // 以下操作就有AB两种情况// A情况: 锁住状态 且 饥饿模式 (自旋次数超过4次)// B情况: 未锁住//拿到最新锁状态new := old// old不是饥饿模式(排除A情况),那是B情况,把new设置成锁状态if old&mutexStarving == 0 {new |= mutexLocked}// old 是 锁住状态 或 是饥饿模式。// 等待数+1 (当前协程加入等待)if old&(mutexLocked|mutexStarving) != 0 {new += 1 << mutexWaiterShift}// 饥饿标识非空 and old是锁住状态。 (第一次进入 且 A情况)// new设置成饥饿状态if starving && old&mutexLocked != 0 {new |= mutexStarving}// awoke标识是 唤醒状态if awoke {// new不是唤醒状态,锁标识不对,panicif new&mutexWoken == 0 {throw("sync: inconsistent mutex state")}// &^ 想异的位保留,相同的位清0。 非唤醒状态 变成 唤醒, 唤醒状态下变成非唤醒new &^= mutexWoken}// 此时new的3个字段状态 : 锁住,饥饿,唤醒状态未知// 如果状态没有被其他协程改变,状态更改成newif atomic.CompareAndSwapInt32(&m.state, old, new) {// 如果状态是非锁住 and 非饥饿模式 // compareAndSwapInt32已经改成锁住,break forif old&(mutexLocked|mutexStarving) == 0 {break // locked the mutex with CAS}// 设置排队者的开始等待时间queueLifo := waitStartTime != 0if waitStartTime == 0 {waitStartTime = runtime_nanotime()}// 信号量设置,阻塞等待(信号量的P操作,协程间通信)runtime_SemacquireMutex(&m.sema, queueLifo, 1)// 标记 饥饿标识, 如果是饥饿标识是true 或者 大于饥饿阈值 starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNs// 获取最新锁状态,虽然前面compareAndSwap已经改成了m.state : 锁住,饥饿,唤醒状态未知。但是前面阻塞有可能其他协程更改了状态old = m.state// 锁是饥饿模式if old&mutexStarving != 0 {// 锁是 锁住状态 或者 唤醒状态 或者 等待者为0个时// 抛出if old&(mutexLocked|mutexWoken) != 0 || old>>mutexWaiterShift == 0 {throw("sync: inconsistent mutex state")}// delta := int32(mutexLocked - 1<<mutexWaiterShift)// 非贪婪模式 或则 等待者为1时if !starving || old>>mutexWaiterShift == 1 {delta -= mutexStarving}atomic.AddInt32(&m.state, delta)break}awoke = trueiter = 0} else {old = m.state}}if race.Enabled {race.Acquire(unsafe.Pointer(m))}
}
func (m *Mutex) Unlock()
:Unlock方法解锁m,如果m未加锁会导致运行时错误。锁和线程无关,可以由不同的线程加锁和解锁。
func (m *Mutex) Unlock() {if race.Enabled {_ = m.staterace.Release(unsafe.Pointer(m))}// Fast path: drop lock bit.new := atomic.AddInt32(&m.state, -mutexLocked)if new != 0 {// Outlined slow path to allow inlining the fast path.// To hide unlockSlow during tracing we skip one extra frame when tracing GoUnblock.m.unlockSlow(new)}
}func (m *Mutex) unlockSlow(new int32) {if (new+mutexLocked)&mutexLocked == 0 {throw("sync: unlock of unlocked mutex")}if new&mutexStarving == 0 {old := newfor {if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken|mutexStarving) != 0 {return}// Grab the right to wake someone.new = (old - 1<<mutexWaiterShift) | mutexWokenif atomic.CompareAndSwapInt32(&m.state, old, new) {runtime_Semrelease(&m.sema, false, 1)return}old = m.state}} else {// 信号量中的V操作runtime_Semrelease(&m.sema, true, 1)}
}
信号量:信号量有两种原子操作,他们必须成对出现
P操作:信号量 减1,当信号量 <0 ,表明资源被占用,进程阻塞。 当信号量>=0,表明资源被释放(可用),进程可继续执行
V操作:信号量加1,当信号量<=0时,代表有阻塞中进程。当信号量>0,表明没有阻塞中进程,无需操作
互斥信号量,默认值为1
————————————————
版权声明:本文为CSDN博主「我是你的小阿磊」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qiu18610714529/article/details/109062176
example
import "sync"func main() {m := sync.Mutex{}go user1(&m)go user2(&m)signalChan := make(chan os.Signal, 1)signal.Notify(signalChan, os.Interrupt)select {case <-signalChan:fmt.Println("catch interrupt signal")break}
}func printer(str string, m *sync.Mutex) {m.Lock() //加锁defer m.Unlock() //解锁for _, ch := range str {fmt.Printf("%c", ch)time.Sleep(time.Millisecond * 1)}
}
func user1(m *sync.Mutex) {printer("hello ", m)
}
func user2(m *sync.Mutex) {printer("world", m)
}//打印结果
worldhello 或者 helloworld: 两个单词是有序的,不像`heworllldo`两个协程同时打印,说明某个协程会在mutex.Lock()进行自旋等待获取锁
RWMutex读写互斥锁
读写互斥锁 sync.RWMutex
是细粒度的互斥锁,它不限制资源的并发读,但是读写、写写操作无法并行执行。
type RWMutex struct {w Mutex // held if there are pending writerswriterSem uint32 // semaphore for writers to wait for completing readersreaderSem uint32 // semaphore for readers to wait for completing writersreaderCount int32 // number of pending readersreaderWait int32 // number of departing readers
}
w
— 复用互斥锁提供的能力;writerSem
和readerSem
— 分别用于写等待读和读等待写:readerCount
存储了当前正在执行的读操作数量;readerWait
表示当写操作被阻塞时等待的读操作个数;
加锁/解锁
-
func (rw *RWMutex) RLock()
:读加锁,如果有写锁,则阻塞等待func (rw *RWMutex) RLock() {if race.Enabled {_ = rw.w.staterace.Disable()}if atomic.AddInt32(&rw.readerCount, 1) < 0 {// 阻塞,等待信号量的v操作释放共享内存,才能获得执行权runtime_SemacquireMutex(&rw.readerSem, false, 0)}if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&rw.readerSem))} }
-
func (rw *RWMutex) RUnlock()
:解读锁,func (rw *RWMutex) RUnlock() {if race.Enabled {_ = rw.w.staterace.ReleaseMerge(unsafe.Pointer(&rw.writerSem))race.Disable()}if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {// Outlined slow-path to allow the fast-path to be inlinedrw.rUnlockSlow(r)}if race.Enabled {race.Enable()} }func (rw *RWMutex) rUnlockSlow(r int32) {if r+1 == 0 || r+1 == -rwmutexMaxReaders {race.Enable()throw("sync: RUnlock of unlocked RWMutex")}// A writer is pending.if atomic.AddInt32(&rw.readerWait, -1) == 0 {// The last reader unblocks the writer.runtime_Semrelease(&rw.writerSem, false, 1)} }
-
func (rw *RWMutex) Lock()
: 写锁,如果有读写锁被占用,阻塞等待所有读写锁释放后才能获得- 其他 Goroutine 在获取写锁时会进入自旋或者休眠
- 有其他 Goroutine 持有互斥锁的读锁,该 Goroutine 会调用
runtime.sync_runtime_SemacquireMutex
进入休眠状态等待所有读锁所有者执行结束后释放writerSem
信号量将当前协程唤醒;
func (rw *RWMutex) Lock() {if race.Enabled {_ = rw.w.staterace.Disable()}// First, resolve competition with other writers.rw.w.Lock()// Announce to readers there is a pending writer.r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders// Wait for active readers.if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {runtime_SemacquireMutex(&rw.writerSem, false, 0)}if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&rw.readerSem))race.Acquire(unsafe.Pointer(&rw.writerSem))}
}
example
func RMutex() {ch := make(chan struct{})rw := &sync.RWMutex{}go func() {rw.RLock()time.Sleep(time.Second * 5)defer rw.RUnlock()fmt.Println("fun1")}()go func() {time.Sleep(time.Millisecond * 500)rw.Lock()defer rw.Unlock()fmt.Println("fun2")close(ch)}()<-ch
}// 先打印出fun1 再打印fun2 代表了读写互斥
相关文章:

go-sync-mutex
Sync Go 语言作为一个原生支持用户态进程(Goroutine)的语言,当提到并发编程、多线程编程时,往往都离不开锁这一概念。锁是一种并发编程中的同步原语(Synchronization Primitives),它能保证多…...

高并发系统设计
高并发系统通用设计方法 Scala-out 横向扩展,分散流量,分布式集群部署 缺点:引入复杂度,节点之间状态维护,节点扩展(上下线) Scala-up 提升单机性能,比如增加内存,增…...

Vue3-Pinia快速入门
1.安装pinia npm install pinia -save 2.在main.js中导入并使用pinia // 导入piniaimport { createPinia } from "pinia"; const pinia createPinia();//使用pinia app.use(pinia)app.mount(#app) 3.在src目录下创建包:store,表示仓库 4…...

Python算法——插入排序
插入排序(Insertion Sort)是一种简单但有效的排序算法,它的基本思想是将数组分成已排序和未排序两部分,然后逐一将未排序部分的元素插入到已排序部分的正确位置。插入排序通常比冒泡排序和选择排序更高效,特别适用于对…...

Java21新特性
目录 一、Java21新特性 1、字符串模版 2、scoped values 3、record pattern 4、switch格式匹配 5、可以在switch中使用when 6、Unnamed Classes and Instance Main Methods 7、Structured Concurrency 一、Java21新特性 1、字符串模版 字符串模版可以让开发者更简洁的…...

4 Tensorflow图像识别模型——数据预处理
上一篇:3 tensorflow构建模型详解-CSDN博客 本篇开始介绍识别猫狗图片的模型,内容较多,会分为多个章节介绍。模型构建还是和之前一样的流程: 数据集准备数据预处理创建模型设置损失函数和优化器训练模型 本篇先介绍数据集准备&am…...

SpringBoot整合RabbitMQ学习笔记
SpringBoot整合RabbitMQ学习笔记 以下三种类型的消息,生产者和消费者需各自启动一个服务,模拟生产者服务发送消息,消费者服务监听消息,分布式开发。 一 Fanout类型信息 . RabbitMQ创建交换机和队列 在RabbitMQ控制台,新…...

在校园跑腿系统小程序中,如何设计高效的实时通知与消息推送系统?
1. 选择合适的消息推送服务 在校园跑腿系统小程序中,选择一个适合的消息推送服务。例如,使用WebSocket技术、Firebase Cloud Messaging (FCM)、或第三方推送服务如Pusher或OneSignal等。注册并获取相关的API密钥或访问令牌。 2. 集成服务到小程序后端…...

求极限Lim x->0 (x-sinx)*e-²x / (1-x)⅓
题目如下: 解题思路: 这题运用了无穷小替换、洛必达法则、求导法则 具体解题思路如下: 1、首先带入x趋近于0,可以得到(0*1)/0,所以可以把e的-x的平方沈略掉 然后根据无穷小替换,利用t趋近于0时…...

JavaScript数据类型详细解析与代码实例
JavaScript是一种弱类型动态语言,数据类型分为原始类型和对象类型。 原始类型 原始类型包括:数字、字符串、布尔值和undefined、null。 数字 JavaScript中的数字类型包括整数和浮点数,可以进行基本的数学运算。 var num1 10; // 整数 v…...

.NET Framework中自带的泛型委托Func
Func<>是.NET Framework中自带的泛型委托,可以接收一个或多个输入参数,并且有返回值,和Action类似,.NET基类库也提供了多达16个输入参数的Func委托,输出参数只有1个。 1、Func泛型委托 .NET Framework为我们提…...

深入理解JVM虚拟机第十七篇:虚拟机栈中栈帧的内部结构
大神链接:作者有幸结识技术大神孙哥为好友,获益匪浅。现在把孙哥视频分享给大家。 孙哥链接:孙哥个人主页 作者简介:一个颜值99分,只比孙哥差一点的程序员 本专栏简介:话不多说,让我们一起干翻JavaScript 本文章简介:话不多说,让我们讲清楚虚拟机栈存储结构和运行原理…...

uniapp中地图定位功能实现的几种方案
1.uniapp自带uni.getLocation uni.getLocation(options) getlocation | uni-app官网 实现思路:uni.getLocation获取经纬度后调用接口获取城市名 优点:方便快捷,直接调用 缺点:关闭定位后延时很久,无法控制定位延迟…...

JS功能实现
目录 轮播图移动端轮播图按下回车发表评论tab栏切换全选按钮 轮播图 <style>* {box-sizing: border-box;}.slider {width: 560px;height: 400px;overflow: hidden;}.slider-wrapper {width: 100%;height: 320px;}.slider-wrapper img {width: 100%;height: 100%;display:…...

connect-history-api-fallback原理
connect-history-api-fallback是一个用于处理前端路由的中间件,它的原理是在服务器接收到请求时,检查请求的路径是否匹配到静态文件(如HTML、CSS、JS等),如果不匹配,则将请求重定向到前端的入口文件&#x…...
Android ConstraintLayout分组堆叠圆角ShapeableImageView
Android ConstraintLayout分组堆叠圆角ShapeableImageView <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"…...

Docker Stack部署应用详解+Tomcat项目部署详细实战
Docker Stack 部署应用 概述 单机模式下,可以使用 Docker Compose 来编排多个服务。Docker Swarm 只能实现对单个服务的简单部署。而Docker Stack 只需对已有的 docker-compose.yml 配置文件稍加改造就可以完成 Docker 集群环境下的多服务编排。 stack是一组共享…...

Compose-Multiplatform在Android和iOS上的实践
本文字数:4680字 预计阅读时间:30分钟 01 简介 之前我们探讨过KMM,即Kotlin Multiplatform Mobile,是Kotlin发布的移动端跨平台框架。当时的结论是KMM提倡将共有的逻辑部分抽出,由KMM封装成Android(Kotlin/JVM)的aar和…...

XXL-JOB 默认 accessToken 身份绕过导致 RCE
文章目录 0x01 漏洞介绍0x02 影响版本0x03 环境搭建0x04 漏洞复现第一步 访问页面返回报错信息第二步 执行POC,进行反弹shell第三步 获取shell0x05 修复建议摘抄免责声明0x01 漏洞介绍 XXL-JOB 是一款开源的分布式任务调度平台,用于实现大规模任务的调度和执行。 XXL-JOB 默…...

7 库函数之复位和时钟设置(RCC)所有函数的介绍及使用
7 库函数之复位和时钟设置(RCC)所有函数的介绍及使用的介绍及使用 1. 图片有格式二、RCC库函数固件库函数预览2.1 函数RCC_DeInit2.2 函数RCC_HSEConfig2.3 函数RCC_WaitForHSEStartUp2.4 函数RCC_AdjustHSICalibrationValue2.5 函数RCC_HSICmd2.6 函数RCC_PLLConfig2.7 函数…...

第十七节——指令
一、概念 在Vue.js中,指令(Directives)是一种特殊的语法,用于为HTML元素添加特定的行为和功能。指令以v-作为前缀,通过在HTML标签中使用这些指令来操作DOM,修改元素的属性、样式或行为。 Vue.js提供了一组…...

优雅的 Dockerfile 是怎样炼成的?
Docker 简介 目前,Docker 主要有两个形态:Docker Desktop 和 Docker Engine。 Docker Desktop 是专门针对个人使用而设计的,支持 Mac(已支持arm架构的M系芯片) 和 Windows 快速安装,具有直观的图形界面&a…...

2023-2024 中国科学引文数据库来源期刊列表(CSCD)
文章目录 CSCD来源期刊遴选报告2023-2024 中国科学引文数据库来源期刊列表(CSCD) CSCD来源期刊遴选报告 2023-2024 中国科学引文数据库来源期刊列表(CSCD)...

【3D图像分割】基于Pytorch的VNet 3D图像分割5(改写数据流篇)
在这篇文章:【3D 图像分割】基于 Pytorch 的 VNet 3D 图像分割2(基础数据流篇) 的最后,我们提到了: 在采用vent模型进行3d数据的分割训练任务中,输入大小是16*96*96,这个的裁剪是放到Dataset类…...

WebSocket Day02 : 握手连接
前言 握手连接是WebSocket建立通信的第一步,通过客户端和服务器之间的一系列握手操作,确保了双方都支持WebSocket协议,并达成一致的通信参数。握手连接的过程包括客户端发起握手请求、服务器响应握手请求以及双方完成握手连接。完成握手连接后…...

c#的反编译工具ISPY和net reflector 使用比较
我有一份Asp.net程序需要修改,但没有源码,只有dll,需要使用反编译工具回复源码,尝试使用了市面上的两种主流的工具ISPY和net reflector ,最终用ISPY恢复了源码。 比较 ISPY 恢复的代码和实际有差距,但还能…...

基于LDA主题+协同过滤+矩阵分解算法的智能电影推荐系统——机器学习算法应用(含python、JavaScript工程源码)+MovieLens数据集(四)
目录 前言总体设计系统整体结构图系统流程图 运行环境模块实现1. 数据爬取及处理2. 模型训练及保存3. 接口实现4. 收集数据5. 界面设计 系统测试相关其它博客工程源代码下载其它资料下载 前言 前段时间,博主分享过关于一篇使用协同过滤算法进行智能电影推荐系统的博…...

方阵行列式与转置矩阵
1.转置矩阵:格式规定:如果矩阵A为n阶方阵,那么A的T次方为矩阵A的转置矩阵,即将矩阵A的行与列互换。 2.转置矩阵的运算性质: 1.任何方阵的转置矩阵的转置矩阵为方阵自身。 2.多个矩阵的和的转置矩阵等于多个转置矩阵的…...

【Java 进阶篇】Java Cookie共享:让数据穿越不同应用的时空隧道
在Web开发中,Cookie是一种常见的会话管理技术,用于存储和传递用户相关的信息。通常,每个Web应用都会在用户的浏览器中设置自己的Cookie,以便在用户与应用之间保持状态。然而,有时我们需要在不同的应用之间共享Cookie数…...

甘特图组件DHTMLX Gantt用例 - 如何拆分任务和里程碑项目路线图
创建一致且引人注意的视觉样式是任何项目管理应用程序的重要要求,这就是为什么我们会在这个系列中继续探索DHTMLX Gantt图库的自定义。在本文中我们将考虑一个新的甘特图定制场景,DHTMLX Gantt组件如何创建一个项目路线图。 DHTMLX Gantt正式版下载 用…...