Go语言中乐观锁与悲观锁的具体使用

 更新时间:2024年01月09日 09:45:28   作者:别人家的孩子zyh  
乐观锁和悲观锁是两种思想,用于解决并发场景下的数据竞争问题,本文主要介绍了Go语言中乐观锁与悲观锁的具体使用,具有一定的参考价值,感兴趣的可以了解一下

改变一个数值的三个步骤

  • 把想修改的数值从某个地方取出来
  • 将取出来的数值修改为期望值
  • 把修改后的数值保存到原来的地方

问题

如果在做第2步时,有另一个过程(进程或线程)对同一个数值进行同样的操作(取值、修改),那么当这两个过程都要做第3步的时候,就肯定有一个过程是白干活的。

悲观锁

悲观的锁总认为会发生并发问题,属于保守派。
如果想修改一个数值,立马给这个数值上一把锁,标明这个数值正在被修改,谁也不能修改了;然后才开始三步走,在三步走的过程结束以后,再把锁解除。

当有其他过程想要修改同一个数值时,看到了锁就不进行三步走了,而是选择等待;当锁被解除了,自己在数值也加一把锁,然后开始三步走,在三个步骤走完了,也把锁解除。

乐观锁

乐观的锁总认为不会发生并发问题,属于乐天派。

修改数据时不加锁,正常进行1、2步,在进行第3步的时候,确认一下数值是否进行了修改,如果被修改过,放弃修改,重新走一遍1、2、3步(或者放弃对数值进行修改)。

Go语言中的乐观锁与悲观锁

sync/atomic

Go语言有一个atomic包,可以在不形成临界区和创建互斥量的情况下完成并发安全的值替换操作,这个包应用的便是乐观锁的原理
但是这个包只支持int32/int64/uint32/uint64/uintptr这几种数据类型的一些基础操作,如增减、交换、载入、存储等

sync

Go语言中的sync包提供了各种锁,如果使用了这个包,基本就以悲观锁的工作模式了

go代码示例

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
)

var (
	x  int64
	mu sync.Mutex
	wg sync.WaitGroup
)

// 普通函数, 并发不安全
func Add() {
	x++
	wg.Done()
}

// 互斥锁, 并发安全,性能低于原子操作
func muAdd() {
	mu.Lock()
	x++
	mu.Unlock()
	wg.Done()
}

// 原子操作,并发安全,性能高于互斥锁,只针对go中的一些基本数据类型使用
func AmAdd() {
	atomic.AddInt64(&x, 1)
	wg.Done()
}

func main() {
	// 原子操作atomic包
	// 加锁操作涉及到内核态的上下文切换, 比较耗时,代价高
	// 针对基本数据类型我们还可以使用原子操作来保证并发安全
	// 因为原子操作是go语言提供的方法,我们在用户态就可以完成,因此性能比加锁操作更好
	// go语言的原子操作由内置的库,sync/atomic完成

	start := time.Now()
	for i := 0; i < 10000; i++ {
		wg.Add(1)
		go Add() // 普通版Add函数不是并发安全的
		// go muAdd() // 加锁版Add函数,是并发安全的, 但是加锁性能开销大
		// go AmAdd() // 原子操作版Add函数,是并发安全的,性能优于加锁版
	}

	end := time.Now()
	wg.Wait()
	fmt.Println(x)
	fmt.Println(end.Sub(start))

}

参考博客1

参考博客2

到此这篇关于Go语言中乐观锁与悲观锁的具体使用的文章就介绍到这了,更多相关Go语言 乐观锁与悲观锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用golang脚本基于kubeadm创建新的token(问题分析)

    使用golang脚本基于kubeadm创建新的token(问题分析)

    这篇文章主要介绍了使用golang脚本基于kubeadm创建新的token(问题分析),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-10-10
  • go语言中的udp协议及TCP通讯实现示例

    go语言中的udp协议及TCP通讯实现示例

    这篇文章主要为大家介绍了go语言中的udp协议及TCP通讯的实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • go语言实现并发网络爬虫的示例代码

    go语言实现并发网络爬虫的示例代码

    本文主要介绍了go语言实现并发网络爬虫的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03
  • Go整合captcha实现验证码功能

    Go整合captcha实现验证码功能

    最近在使用Go语言搞一个用户登录&注册的功能,我们油然会产生一种增加验证码的想法。后来在GitHub上找到了这个名叫captcha的插件,于是就利用文档进行了初步的学习,并融入到自己的项目中,整个过程下来感觉这个插件的设计非常巧妙
    2023-03-03
  • 记一次go语言使用time.Duration类型踩过的坑

    记一次go语言使用time.Duration类型踩过的坑

    本文主要介绍了记一次go语言使用time.Duration类型踩过的坑,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • Go实现一个配置包详解

    Go实现一个配置包详解

    在现代软件开发中,配置文件是不可或缺的一部分。在编写 Go 项目时,程序的灵活性和可扩展性都需要依赖于配置文件的加载。本文就来探究下在 Go 项目中如何更加方便的加载和管理配置,感兴趣的朋友跟着小编一起来学习吧
    2023-04-04
  • Golang使用embed引入静态文件的实例代码

    Golang使用embed引入静态文件的实例代码

    Golang embed是Go 1.16版本中引入的一项新功能,它可以使嵌入文件更容易,通常,在Go中嵌入文件需要使用文件系统或者第三方包,而使用embed可以更加便捷地嵌入文件,从而方便地访问文件的内容,本文介绍了Golang使用embed引入静态文件,需要的朋友可以参考下
    2024-06-06
  • Golang加密解密之RSA(附带php)

    Golang加密解密之RSA(附带php)

    安全总是很重要的,各个语言对于通用的加密算法都会有实现。本文先是对RSA算法进行了简单介绍,后才进行介绍如何用Go实现RSA的加密解密,下面一起来看看吧。
    2016-08-08
  • GO语言延迟函数defer用法分析

    GO语言延迟函数defer用法分析

    这篇文章主要介绍了GO语言延迟函数defer用法,较为详细的分析了GO语言的特性与具体用法,并给出了一个比较典型的应用实例,具有一定的参考借鉴价值,需要的朋友可以参考下
    2014-12-12
  • golang如何用type-switch判断interface变量的实际存储类型

    golang如何用type-switch判断interface变量的实际存储类型

    这篇文章主要介绍了golang如何用type-switch判断interface变量的实际存储类型,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-04-04

最新评论