iis服务器助手广告
返回顶部
首页 > 资讯 > 后端开发 > GO >Golang的锁机制与使用技巧小结
  • 458
分享到

Golang的锁机制与使用技巧小结

2024-04-02 19:04:59 458人浏览 薄情痞子
摘要

目录1. sync.Mutex详解2. RWMutex详解3. sync.Map详解4. 原子操作 atomic.Value5. 使用小技巧1. sync.Mutex详解 sync.

1. sync.Mutex详解

sync.MutexGo中的互斥,通过.lock()方法上锁,.unlock()方法解锁。需要注意的是,因为Go函数值传递的特点,sync.Mutex通过函数传递时,会进行一次拷贝,所以传递过去的锁是一把全新的锁,大家在使用时要注意这一点,另外sync.Mutex是非重入锁,这一点要与Java中的锁区分。

type Mutex {
    state int32
    sema  uint32
}

上面数据结构中的state最低三位分别表示 mutexLocked、mutexWoken 和 mutexStarving,剩下的位置用来表示当前有多少个 Goroutine 等待互斥锁的释放:

32                                               3             2             1             0 
 |                                               |             |             |             | 
 |                                               |             |             |             | 
 v-----------------------------------------------v-------------v-------------v-------------+ 
 |                                               |             |             |             v 
 |                 waitersCount                  |mutexStarving| mutexWoken  | mutexLocked | 
 |                                               |             |             |             | 
 +-----------------------------------------------+-------------+-------------+-------------+                                                                                                              
  • mutexLocked — 表示互斥锁的锁定状态;
  • mutexWoken — 表示从正常模式被从唤醒;
  • mutexStarving — 当前的互斥锁进入饥饿状态;
  • waitersCount — 当前互斥锁上等待的 goroutine 个数;

2. RWMutex详解

type RWMutex struct {
	w           Mutex  // 复用互斥锁
	writerSem   uint32 // 写锁监听读锁释放的信号量
	readerSem   uint32 // 读锁监听写锁释放的信号量
	readerCount int32  // 当前正在执行读操作的数量
	readerWait  int32  // 当写操作被阻塞时,需要等待读操作完成的个数
}
  • 读操作如何防止并发读写问题的?

RLock(): 申请读锁,每次执行此函数后,会对readerCount++,此时当有写操作执行Lock()时会判断readerCount>0,就会阻塞。

RUnLock(): 解除读锁,执行readerCount–,释放信号量唤醒等待写操作的goroutine。

  • 写操作如何防止并发读写、并发写写问题?

Lock(): 申请写锁,获取互斥锁,此时会阻塞其他的写操作。并将readerCount 置为 -1,当有读操作进来,发现readerCount = -1, 即知道有写操作在进行,阻塞。

Unlock(): 解除写锁,会先通知所有阻塞的读操作goroutine,然后才会释放持有的互斥锁。

  • 写操作的饥饿问题?

这是由于写操作要等待读操作结束后才可以获得锁,而写操作在等待期间可能还有新的读操作持续到来,如果写操作等待所有读操作结束,很可能会一直阻塞,这种现象称之为写操作被饿死。

通过RWMutex结构体中的readerWait属性可完美解决这个问题。

当写操作到来时,会把RWMutex.readerCount值拷贝到RWMutex.readerWait中,用于标记排在写操作前面的读者个数。

前面的读操作结束后,除了会递减RWMutex.readerCount,还会递减RWMutex.readerWait值,当RWMutex.readerWait值变为0时唤醒写操作。

3. sync.Map详解

一般情况下解决并发读写 map 的思路是加一把大锁,或者把一个 map 分成若干个小 map,对 key 进行哈希,只操作相应的小 map。前者锁的粒度比较大,影响效率;后者实现起来比较复杂,容易出错。

而使用 sync.map 之后,对 map 的读写,不需要加锁。并且它通过空间换时间的方式,使用 read 和 dirty 两个 map 来进行读写分离,降低锁时间来提高效率。

type Map struct {
	mu Mutex
	read atomic.Value // readOnly
	dirty map[interface{}]*entry
	misses int
}

// readOnly is an immutable struct stored atomically in the Map.read field.
type readOnly struct {
	m       map[interface{}]*entry
	amended bool // true if the dirty map contains some key not in m.
}

type entry struct {
	p unsafe.Pointer // *interface{}
}

在进行读操作的时候,会先在read中找,没有命中的话会锁住dirty并且寻找,如果找到了miss计数+1,超过阈值时将dirty赋值给read;

在进行添加操作时,直接在dirty中添加;

在进行修改操作时,先改read,再改dirty;

在进行删除操作时,将read中加上amended标记,dirty中直接删除。

4. 原子操作 atomic.Value

愿此操作的底层是靠 MESI 缓存一致性协议来维持的。

Go的 atomic.Value 需要注意应该放入只读对象。

//atomic.Value源码

type Value struct {
	v interface{} // 所以可以存储任何类型的数据
}

// 空 interface{} 的内部表示格式,作用是将interface{}类型分解,得到其中两个字段
type ifaceWords struct {
	typ  unsafe.Pointer
	data unsafe.Pointer
}

// 取数据就是正常走流程
func (v *Value) Load() (x interface{}) {
	vp := (*ifaceWords)(unsafe.Pointer(v))
	typ := LoadPointer(&vp.typ)
	if typ == nil || uintptr(typ) == ^uintptr(0) {
		// 第一次还没写入
		return nil
	}
  // 构造新的interface{}返回出去
	data := LoadPointer(&vp.data)
	xp := (*ifaceWords)(unsafe.Pointer(&x))
	xp.typ = typ
	xp.data = data
	return
}

// 写数据(如何保证数据完整性)
func (v *Value) Store(x interface{}) {
	if x == nil {
		panic("sync/atomic: store of nil value into Value")
	}
  // 绕过 Go 语言类型系统的检查,与任意的指针类型互相转换
	vp := (*ifaceWords)(unsafe.Pointer(v)) // 旧值
	xp := (*ifaceWords)(unsafe.Pointer(&x)) // 新值
	for { // 配合CompareAndSwap达到乐观锁的功效
		typ := LoadPointer(&vp.typ)
		if typ == nil { // 第一次写入
			runtime_procPin() // 禁止抢占
			if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) {
				runtime_procUnpin() // 没有抢到锁,说明已经有别的线程抢先完成赋值,重新进入循环
				continue
			}
			// 首次赋值
			StorePointer(&vp.data, xp.data)
			StorePointer(&vp.typ, xp.typ)
			runtime_procUnpin() // 写入成功,解除占用状态
			return
		}
		if uintptr(typ) == ^uintptr(0) {
			// 第一次写入还未完成,继续等待
			continue
		}
		// 两次需要写入相同类型
		if typ != xp.typ {
			panic("sync/atomic: store of inconsistently typed value into Value")
		}
		StorePointer(&vp.data, xp.data)
		return
	}
}

// 禁止抢占,标记当前G在M上不会被抢占,并返回当前所在P的ID。
func runtime_procPin()
// 解除G的禁止抢占状态,之后G可被抢占。
func runtime_procUnpin()

5. 使用小技巧

  • 减小临界区域(减少锁的持有时间)
var m sync.Mutex

func DoSth() {
    // do sth1
    func() {
       u.lock()
       defer m.unlock()
       // do sth2
    }() 
    // do sth3
}

如上所示,如果do sth3中是很费时的io操作,使用这个技巧可以将临界区减小,提高性能,不过,如果本身临界区就不大,锁操作后续没有什么费时操作,那么也就没有必要这样操作了。

  • 减小锁的粒度

高并发场景下,用锁的数量来换取并发效率,类似于java中ConcurrentHashMap的分段锁思想,增加锁的数量,减少一把锁控制的数据量。

  • 读写分离(读写锁): RWMutex,sync.Map

在读多写少的情景下,可以使用读写锁,提高读操作的并发性能。

  • 使用原子操作

原子操作是CPU指令级的操作,不会触发g调度机制。,不阻塞执行流

到此这篇关于golang的锁机制与使用技巧精选的文章就介绍到这了,更多相关Golang 锁机制内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

您可能感兴趣的文档:

--结束END--

本文标题: Golang的锁机制与使用技巧小结

本文链接: https://www.lsjlt.com/news/150382.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Golang的锁机制与使用技巧小结
    目录1. sync.Mutex详解2. RWMutex详解3. sync.Map详解4. 原子操作 atomic.Value5. 使用小技巧1. sync.Mutex详解 sync....
    99+
    2024-04-02
  • Golang的锁机制与使用技巧是什么
    本篇内容主要讲解“Golang的锁机制与使用技巧是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Golang的锁机制与使用技巧是什么”吧!1. sync.Mutex详解sync.Mutex是...
    99+
    2023-06-30
  • Golang中锁机制的性能优化技巧
    在Golang中,锁(Mutex)是一种常用的并发控制机制,但在某些情况下,使用锁可能会导致性能问题。以下是一些Golang中锁机制...
    99+
    2023-10-08
    Golang
  • Golang的锁机制使用及说明
    目录踩坑点互斥锁 Mutex读写锁 RWMutex谨防锁拷贝查看数据竞争总结golang中的锁分为互斥锁、读写锁、原子锁即原子操作。 在 Golang 里有专门的方法来实现锁,就是 ...
    99+
    2023-02-16
    Golang锁机制 锁机制 Golang锁
  • Mybatis小技巧中的别名机制
    文章目录 一、Mybatis中的别名机制1.方式一2.方式二3.方式三 总结 一、Mybatis中的别名机制 在Mybatis的核心配置文件当中有一个标签用来起别名 起别名之后在Ma...
    99+
    2023-09-05
    mybatis java mysql
  • Golang方法的使用与优化技巧
    Golang方法的使用与优化技巧 在Go语言编程中,方法是一种特殊类型的函数,它是一个与对象关联的函数。本文将通过具体的代码示例介绍Golang方法的使用和优化技巧,让读者更好地掌握这...
    99+
    2024-02-23
    使用 优化技巧 golang方法 go语言
  • Android中Glide库的使用小技巧总结
    简介在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫 Glide 的图片加载库,作者是bumptech。这个库被广泛的运用在google的开源项目中,包括2014年google I/O大会上发布的官方app。https://githu...
    99+
    2023-05-30
    android glide库 使用技巧
  • vue3provide与inject的使用小技巧分享
    目录vue3 provide与inject使用技巧进入正题父组件child组件son组件vue3的一些实用技巧v-for 和 v-if 不要一起使用(Vue2)vue3 provid...
    99+
    2024-04-02
  • Golang中同步机制的性能调优技巧与经验分享
    在Golang中,使用同步机制(如互斥锁,读写锁等)是保证并发安全的重要手段。然而,不正确的同步机制使用可能导致性能问题。下面是一些...
    99+
    2023-10-08
    Golang
  • 使用Golang判断字符串结尾字符的技巧
    使用Golang判断字符串结尾字符的技巧 在Golang中,判断字符串的结尾字符是一种常见的操作。通过使用strings包提供的函数,我们可以轻松实现这一功能。下面将介绍一些常用的技巧...
    99+
    2024-03-12
    字符串 golang 结尾
  • 分享MySQL中锁的使用技巧
    MySQL 锁的使用技巧分享随着数据库应用的日益广泛,对数据库的并发控制和数据完整性要求也越来越高。在MySQL数据库中,锁是一种重要的并发控制手段,可以有效地保护数据的完整性和一致性。本文将对MySQL锁的使用技巧进行详细分享,并提供具体...
    99+
    2023-12-21
    MySQL 技巧
  • 高级Golang包的使用技巧
    Golang包的高级应用技巧 在使用Golang开发应用程序时,包是非常重要的概念。包可以帮助我们组织代码,并提供了可重用和封装的功能。除了基本的应用场景,Golang包还有一些高级的应用技巧,可以让我们更加...
    99+
    2024-01-16
  • Golang Facade模式的使用技巧与注意事项
    使用技巧:1. 理解Facade模式的目的:Facade模式旨在为外部客户端提供一个简单的接口,隐藏系统内部的复杂性。在使用Faca...
    99+
    2023-10-20
    Golang
  • 掌握Golang包的使用技巧
    深入理解Golang包的使用方法,需要具体代码示例 在Go语言中,包(package)是组织和管理代码的基本单元。一个包可以包含多个Go源文件(以.go为扩展名),这些文件共同构成了一个功能单元。包的使用方式...
    99+
    2024-01-16
  • 在Mac上使用Golang编程的步骤与技巧
    标题:在Mac上使用Golang编程的步骤与技巧 在当前软件开发领域,Golang(也被称为Go)作为一种高效、简洁、并发性强的编程语言,受到越来越多开发者的关注和使用。在Mac平台上...
    99+
    2024-03-03
    编写程序 安装golang 配置环境 golang开发
  • ECharts和golang: 制作实用的统计图表的技巧与经验
    ECharts和golang: 制作实用的统计图表的技巧与经验,需要具体代码示例随着数据处理的普及,数据可视化成为了许多应用领域的必备技能。而制作图表的工具也应运而生。ECharts是一个开源的数据可视化工具,而golang是一种高效率和高...
    99+
    2023-12-17
    Golang echarts 统计图表
  • golang函数并发控制与锁机制之间的比较与选择
    函数级并发控制和锁机制是 go 中控制并发的两种机制。函数级并发简单易用,但无法保证执行顺序。锁机制提供更精细的控制,防止数据竞争,但更复杂。选择机制取决于用例:使用函数级并发控制时,任...
    99+
    2024-04-24
    golang 并发 锁机制 并发访问
  • Angular项目中使用scss文件的一些技巧小结
    目录使用 Angular CLI 新建一个 Angular 项目:Angular 项目中导入 node_modules 文件夹下文件的一些技巧附:angular5 配置使用sass总...
    99+
    2024-04-02
  • 流量控制在Golang中的应用技巧
    流量控制在Golang中的应用技巧 随着网络应用的发展,流量控制变得越来越重要。在Go语言中,通过一些技巧,我们可以有效地进行流量控制,保障系统的稳定性和性能。本文将介绍在Golang...
    99+
    2024-03-06
    技巧 golang 流控制 go语言
  • Pandas与Docker的使用技巧
    本篇内容介绍了“Pandas与Docker的使用技巧”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!pandas 读取无头 CSV我们知道,C...
    99+
    2023-06-15
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作