Go中为什么不建议用锁?

在这里插入图片描述

Go语言中是不建议用锁,而是用通道Channel来代替(不要通过共享内存来通信,而通过通信来共享内存),当然锁也是可以用,锁是防止同一时刻多个goroutine操作同一个资源;

GO语言中,要传递某个数据给另一个goroutine(协程),可以把这个数据封装成一个对象,然后把这个对象的指针传入某个channel中,另外一个goroutine从这个channel中读出这个指针,并处理其指向的内存对象。GO从语言层面保证同一个时间只有一个goroutine能够访问channel里面的数据,为开发者提供了一种优雅简单的工具,所以GO的做法就是使用channel来通信,通过通信来传递内存数据,使得内存数据在不同的goroutine中传递,而不是使用共享内存来通信。

加锁操作通常通过 sync 包中的 Mutex 类型来实现。Mutex(互斥锁)是一种最基本的锁机制,用于保护共享资源,确保在同一时间只有一个 goroutine 可以访问共享资源,从而避免数据竞争和并发问题。

加锁的场景,通过合理地使用锁,可以确保并发程序的正确性和稳定性,避免出现数据竞争和其他并发问题。

  1. 共享数据的读写保护: 当多个 goroutine 需要同时读写共享的数据时,为了保证数据的一致性和正确性,需要使用锁来对共享数据进行读写保护。通过加锁操作,可以确保在同一时间只有一个 goroutine 可以对共享数据进行写操作,避免出现数据竞争和并发问题。
  2. 临界区保护: 当某个代码块需要被多个 goroutine 同时访问时,为了避免多个 goroutine 同时进入临界区而导致的问题,可以使用锁来对临界区进行保护。通过在临界区的入口处加锁,在出口处解锁,可以确保在同一时间只有一个 goroutine 可以执行临界区的代码。
  3. 资源的同步访问: 在某些场景下,多个 goroutine 需要对某个资源进行同步访问,例如在并发编程中常见的信号量、互斥锁等同步机制。通过使用锁来控制资源的访问,可以保证多个 goroutine 之间的操作是有序的,避免出现数据不一致或其他并发问题。
  4. 并发数据结构的实现: 在实现并发安全的数据结构时,如并发安全的队列、栈、哈希表等,通常需要使用锁来对数据结构进行加锁保护,以确保在并发环境中的安全访问。通过使用锁来控制并发访问,可以实现高效并发的数据结构操作。
  5. 避免竞态条件: 竞态条件是指当多个 goroutine 同时访问共享资源时,由于执行顺序的不确定性而导致的程序行为不确定的情况。为了避免竞态条件,可以使用锁来对共享资源进行加锁保护,确保每次操作的原子性和一致性。

我们来认识几种加锁和不加锁的使用;

1、读写互斥锁

应用场景

适用于读多写少的场景下,才能提高程序的执行效率.

特点

  1. 读的goroutine来了获取的是读锁,后续的goroutine能读不能写
  2. 写的goroutine来了获取的是写锁,后续的goroutine不管是读还是写都要等待获取锁

使用

package mainimport ("fmt""sync""time"
)func main() {var mu sync.Mutex // 定义一个互斥锁var counter int// 启动多个 goroutine 并发地增加计数器的值for i := 0; i < 5; i++ {go func() {for j := 0; j < 1000; j++ {// 在访问共享资源之前先加锁mu.Lock()counter++// 完成对共享资源的访问后释放锁mu.Unlock()}}()}// 等待所有 goroutine 完成time.Sleep(time.Second)// 打印最终计数器的值fmt.Println("Final Counter:", counter)
}# 说明
var rwLock sync.RWMutex
rwLock.RLock() // 获取读锁
rwLock.RUnlock() // 释放读写rwLock.Lock() // 获取写锁
rwLock.Unlock() // 释放写锁

以下介绍不需要用户额外加锁的并发操作

2、等待组

应用场景

sync.Waitgroup是一种同步原语,

用来等groutine执行完再继续,是一个结构体.是值类型.给函数传参数的时候要传指针.

  • 控制程序的并发流程
  • 监控程序执行完成状态
  • 等待一组 goroutine 完成任务
  • 资源等待和释放

特点

  • WaitGroup 是线程安全的,内部使用原子操作,无需额外加锁。

使用

package mainimport ("fmt""sync""time"
)func main() {var wg sync.WaitGroupfor i := 0; i < 3; i++ {wg.Add(1) // 添加一个 goroutine 到计数器go func(id int) {defer wg.Done() // goroutine 完成任务后减少计数器fmt.Printf("Goroutine %d starting\n", id)time.Sleep(time.Second) // 模拟任务执行fmt.Printf("Goroutine %d done\n", id)}(i)}fmt.Println("Main goroutine waiting for other goroutines to finish...")wg.Wait() // 等待所有 goroutine 完成任务fmt.Println("All goroutines finished.")
}# 说明
wg.Add(1) // 起几个goroutine就加几个计数
wg.Done() // 在goroutine对应的函数中,函数要结束的时候表示goroutine完成,计数器-1
wg.Wait() // 阻塞,等待所有的goroutine都结束

2、Sync.Once

使用场景

某些函数只需要执行一次的时候,就可以使用sync.Once

比如 blog加载图片那个例子

var once sync.Onceonce.Do() // 接受一个没有参数也没有返回值的函数,如有需要可以使用闭包

特点

  • 用于执行某个函数且确保只执行一次,通常用于初始化操作

使用

package mainimport ("fmt""sync"
)func main() {var once sync.Once// 定义一个初始化函数,只会被执行一次initialize := func() {fmt.Println("Initializing...")}// 开启多个 goroutine 同时调用初始化函数for i := 0; i < 3; i++ {go func() {once.Do(initialize) // 使用 sync.Once 确保初始化函数只被执行一次}()}fmt.Println("Main goroutine waiting...")
}

3、sync.Map

使用场景

  • 缓存系统: 在缓存系统中,sync.Map 可以用来存储缓存数据,以供多个 goroutine 并发访问。它可以在不需要额外的锁机制的情况下提供并发安全的缓存存储和访问,从而提高缓存系统的性能和并发能力。
  • 全局状态管理: 在需要跨多个 goroutine 共享状态的应用程序中,sync.Map 可以用来管理全局状态。例如,一个 Web 服务器中可以使用 sync.Map 来存储用户的会话状态或其他全局状态信息。
  • 动态配置管理: 在一些需要动态加载和更新配置信息的应用程序中,sync.Map 可以用来存储配置信息,并提供并发安全的访问和更新接口。这样可以保证在配置更新的过程中不会出现数据竞争或其他并发问题。
  • 任务调度器: 在任务调度器中,sync.Map 可以用来存储任务的执行状态或其他相关信息。多个 goroutine 可以并发地读取和更新任务状态,而无需额外的锁机制,从而提高任务调度器的并发能力和性能。
  • 分布式系统中的局部缓存: 在分布式系统中,每个节点可能需要维护一个局部缓存来存储部分数据,sync.Map 可以作为局部缓存的实现。每个节点的局部缓存可以独立地进行读写操作,而无需与其他节点进行同步,从而提高系统的响应速度和吞吐量。

特点

sync.Map 是 Go 语言标准库 sync 包中提供的一种并发安全的键值对映射类型。与普通的 map 不同,sync.Map 在并发访问时不需要额外的锁机制,因此在并发场景下具有更好的性能。

使用

是一个开箱即用(不需要make初始化)的并发安全的map,

package mainimport ("fmt""sync"
)func main() {var m sync.Map// 使用 Store 方法向 sync.Map 中存储键值对m.Store("key1", "value1")m.Store("key2", "value2")m.Store("key3", "value3")// 使用 Load 方法从 sync.Map 中加载键对应的值if value, ok := m.Load("key1"); ok {fmt.Println("Value for key1:", value)} else {fmt.Println("Key1 not found")}// 使用 Range 方法遍历 sync.Map 中的所有键值对fmt.Println("All key-value pairs:")m.Range(func(key, value interface{}) bool {fmt.Println("Key:", key, "Value:", value)return true // 返回 true 继续遍历,返回 false 中止遍历})// 使用 Delete 方法从 sync.Map 中删除键值对m.Delete("key2")// 检查是否包含某个键fmt.Println("Contains key3?", m.Load("key3"))// 清空 sync.Mapm.Range(func(key, value interface{}) bool {m.Delete(key)return true})
}# 说明
// Map[key] = value // 原生map
syncMap.Store(key, value)
syncMap.Load(key)
syncMap.LoadOrStore()
syncMap.Delete()
syncMap.Range()

4、原子操作

Go语言内置了一些针对内置的基本数据类型的一些并发安全的操作;使用场景想用就用

特点

  1. 原子性: 原子操作是不可分割的,要么完全执行成功,要么完全不执行。在执行原子操作期间,不会被中断,也不会被其他 goroutine 所干扰。
  2. 并发安全: 原子操作是并发安全的,可以在多个 goroutine 并发访问时保证数据的一致性和正确性。即使多个 goroutine 同时对共享数据执行原子操作,也不会出现竞态条件(race condition)或数据竞争问题。
  3. 性能高效: 原子操作通常使用底层硬件的原子指令来实现,因此性能较高。相比于加锁机制,原子操作不需要额外的锁和同步机制,可以更快地完成操作。
  4. 适用范围广泛: 原子操作可以用于对各种类型的数据进行操作,如整型、指针等。它们可以在不同的并发场景下使用,如计数器递增、比较并交换、加载、存储等操作。
  5. 简单易用: Go 语言标准库中提供了一系列原子操作函数,使用起来非常简单直观。通过调用这些函数,开发者可以轻松地在并发程序中实现原子操作,而无需过多考虑并发安全性的问题。

使用

package mainimport ("fmt""sync""sync/atomic"
)func main() {var counter int64 // 使用 int64 类型的计数器var wg sync.WaitGroupconst numGoroutines = 10wg.Add(numGoroutines)// 多个 goroutine 并发地对计数器进行增加操作for i := 0; i < numGoroutines; i++ {go func() {for j := 0; j < 1000; j++ {atomic.AddInt64(&counter, 1) // 使用原子的增加操作}wg.Done()}()}wg.Wait() // 等待所有 goroutine 完成fmt.Println("Final Counter:", counter)
}

5、Channel

使用场景

  • goroutine 通信: Channel 是 goroutine 之间进行通信的主要方式。通过 channel,不同的 goroutine 可以安全地共享数据、进行同步操作,从而实现并发编程中的任务协作。
  • 工作池: 可以使用 channel 来实现工作池模式,将任务发送到一个任务队列中,由固定数量的 worker goroutine 来处理任务。通过 channel,可以很方便地控制 worker goroutine 的数量和任务的调度。
  • 事件通知: 可以使用 channel 来实现事件通知机制,一个 goroutine 可以向 channel 中发送事件,而其他 goroutine 可以通过监听 channel 来获取事件并进行相应的处理。
  • 计算结果收集: 在并发计算中,可以使用 channel 来收集各个 goroutine 计算得到的结果,并在所有结果都就绪后进行汇总或其他操作。
  • 超时控制: 可以使用 channel 来实现超时控制机制,例如通过 time.After 函数返回的 channel 来实现某个操作的超时判断。

特点

  • 安全性: Channel 是并发安全的,多个 goroutine 可以同时对一个 channel 进行读写操作,而不会发生数据竞争或其他并发问题。这是因为 channel 内部实现了同步机制,能够确保数据传递的安全性。
  • 阻塞操作: 当向一个已满的 channel 发送数据时,发送操作会阻塞直到有其他 goroutine 从该 channel 中接收数据;当从一个空的 channel 接收数据时,接收操作会阻塞直到有其他 goroutine 向该 channel 发送数据。这种阻塞操作使得 goroutine 之间的通信更加简洁和可靠。
  • 单向传输: Channel 支持单向传输,即可以指定 channel 只能用于发送数据或只能用于接收数据。这样可以在一定程度上增强代码的可读性和安全性。
  • 关闭通知: 可以通过关闭 channel 来向接收方通知数据流的结束。接收方可以通过检查 channel 的关闭状态来判断是否还有数据需要处理。
  • 引用类型: Channel 是引用类型,可以像其他引用类型一样进行传递、赋值和比较。这使得在函数间传递 channel 变得非常方便。

使用

package mainimport ("fmt""time"
)func sender(ch chan<- int) {for i := 0; i < 5; i++ {ch <- i // 向通道发送数据time.Sleep(time.Second)}close(ch) // 关闭通道
}func receiver(ch <-chan int) {for num := range ch { // 从通道接收数据,直到通道关闭fmt.Println("Received:", num)}
}func main() {ch := make(chan int) // 创建一个整型通道go sender(ch)   // 启动发送数据的 goroutinego receiver(ch) // 启动接收数据的 goroutinetime.Sleep(6 * time.Second) // 等待一段时间,确保 goroutine 有足够的时间执行
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/5374.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript原型链深度剖析

目录 前言 一、原型链 1.原型链的主要组成 原型&#xff08;Prototype&#xff09; 构造函数&#xff08;Constructor&#xff09; 实例&#xff08;Instance&#xff09; 2.原型链的工作原理 前言 在JavaScript的世界中&#xff0c;原型链&#xff08;Prototype Chain&…

R语言的学习——day1

将数据框中某一列数据改成行名 代码 结果

.net core ef 连表查询

Information和TypeInfo连表查询 类似&#xff1a; select st.Title1,si.* from [Star_Information] si left join Star_TypeInfo st on si.typeId2st.id 先在EfCoreDbContext.cs配置 protected override void OnModelCreating(ModelBuilder builder){base.OnModelCreating(b…

基于SSM的文物管理系统(含源码+sql+视频导入教程+文档+PPT)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的文物管理系统拥有俩种角色 管理员&#xff1a;个人信息管理、用户管理、分类管理、文物信息管理、文物外借管理、文物维修管理、留言板管理等 用户&#xff1a;登录注册、分类…

[华为OD] C卷 服务器cpu交换 现有两组服务器QA和B,每组有多个算力不同的CPU 100

题目&#xff1a; 现有两组服务器QA和B,每组有多个算力不同的CPU,其中A[i]是A组第i个CPU的运算能 力&#xff0c;B[i]是B组第i个CPU的运算能力。一组服务器的总算力是各CPU的算力之和。 为了让两组服务器的算力相等&#xff0c;允许从每组各选出一个CPU进行一次交换。 求两…

Linux 权限的简单讲解

1、前言 当我们分别使用 touch、mkdir 命令创建一名为 test1 的文件和名为 test2 的目录&#xff0c;发现其中有些参数不一样&#xff0c;本文就来给大家来剖析一下。 2、 参数讲解 我们可以通过切片分为下面几个区域&#xff0c;本文就只简单讲解文件类型、权限、所属用户、所…

CGAL 网格热力图

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 这里实现一个很有趣的功能,生成网格热力图,思路其实很简单:通过指定一个点,计算网格其他点到指定点的测地线距离,以此来为每个网格顶点进行赋色即可。 二、实现代码 //CGAL #include <CGAL/Simple_cartesi…

用HTML5实现播放gif文件

用HTML5实现播放gif文件 在HTML5中&#xff0c;你可以使用<img>标签来播放GIF文件。GIF文件本质上是一种图像格式&#xff0c;它支持动画效果&#xff0c;因此当在网页上加载时&#xff0c;它会自动播放动画。先看一个简单的示例&#xff1a; <!DOCTYPE html> &l…

Elasticsearch:探索 11 种流行的机器学习算法

作者&#xff1a;来自 Elastic Elastic Platform Team 过去几年中&#xff0c;机器学习&#xff08;ML&#xff09;已经悄然成为我们日常生活中不可或缺的一部分。它影响着从购物网站和流媒体网站上的个性化推荐&#xff0c;到保护我们的收件箱免受我们每天收到的大量垃圾邮件的…

2024年第二十六届“华东杯”(B题)大学生数学建模挑战赛|数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 让我们来看看华东杯 (B题&#xff09;&#xff01; 第一个问题…

神经网络与深度学习(四)--自然语言处理NLP

这里写目录标题 1.序列模型2.数据预处理2.1特征编码2.2文本处理 3.文本预处理与词嵌入3.1文本预处理3.2文本嵌入 3.RNN模型3.1RNN概要3.2RNN误差反传 4.门控循环单元&#xff08;GRU&#xff09;4.1GRU基本结构 5.长短期记忆网络 (LSTM) 1.序列模型 分类问题与预测问题 图像分…

java版本共存与fastjson反序列化rmi服务器的搭建

文章目录 java 8下载远程加载类工具编译工具mvn多版本共存配置mvn编译marshalsec编译rce文件利用marshalsec加载远程RCE类 java 8下载 链接&#xff1a;https://pan.baidu.com/s/1B8U9v8QAe4Vc67Q84_nqcg?pwd0000 提取码&#xff1a;0000 远程加载类工具 https://github.co…

Cesium 3dTileset 支持 uv 和 纹理贴图

原理: 使用自定义shader实现uv自动计算 贴图效果: uv效果:

(Microsoft SQL Server,错误: 233)

错误信息: A connection was successfully established with the server, but then an error occurred during the pre-login handshake. (provider: Shared Memory Provider, error: 0 - 管道的另一端上无任何进程。) (Microsoft SQL Server&#xff0c;错误: 233) 原因&…

vue 设置输入框只能输入数字且只能输入小数点后两位,并且不能输入减号

<el-input v-model.trim"sb.price" placeholder"现价" class"input_w3" oninput"valuevalue.replace(/[^0-9.]/g,).replace(/\.{2,}/g,.).replace(/^(\-)*(\d)\.(\d\d).*$/,$1$2.$3)"/> 嘎嘎简单、、、、、、、、、

自定义之道:学习 Java 中如何打磨独特的异常

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一个人虽可以走的更快&#xff0c;但一群人可以走的更远。 我是一名后…

智能科技的飞跃:LLAMA3引领的人工智能新时代

大家好&#xff01;相信大家对于AI&#xff08;人工智能&#xff09;的发展已经有了一定的了解&#xff0c;但你是否意识到&#xff0c;到了2024年&#xff0c;AI已经变得如此强大和普及&#xff0c;带来了我们从未想象过的便利和创新呢&#xff1f;让我们一起来看看AI在这个时…

3D看车有哪些强大的功能?适合哪些企业使用?

3D看车是一种创新的汽车展示方式&#xff0c;它提供了许多强大的功能&#xff0c;特别适合汽车行业的企业使用。 3D看车可实现哪些功能&#xff1f; 1、细节展示&#xff1a; 51建模网提供全套汽车行业3D数字化解决方案&#xff0c;3D看车能够将汽车展示得更加栩栩如生&…

maven聚合,继承等方式

需要install安装到本地仓库&#xff0c;或者私服&#xff0c;方可使用自己封装项目 编译&#xff0c;测试&#xff0c;打包&#xff0c;安装&#xff0c;发布 parent: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://mav…

Open CASCADE学习|BRepFill_SectionPlacement

BRepFill_SectionPlacement 是一个与计算机辅助设计&#xff08;CAD&#xff09;相关的术语&#xff0c;通常用于指代一个几何对象或操作&#xff0c;它是Open CASCADE Technology&#xff08;OCCT&#xff09;中的一个类。Open CASCADE Technology是一个开源的CAD内核&#xf…