Golang 并发机制-4:用Mutex管理共享资源

ops/2025/2/3 8:28:24/

并发性是Go的强大功能之一,它允许多个线程(并发线程)同时执行。然而,权力越大,责任越大。当多个例程并发地访问和修改共享资源时,可能会导致数据损坏、竞争条件和不可预测的程序行为。为了解决这些问题,Go提供了一个称为互斥的同步原语(互斥的缩写)。在本文中,我们将探讨Mutex在管理共享资源中的作用,以及在并发编程中使用它的必要性。

互斥锁简介

互斥锁是一种同步原语,提供对共享资源或代码关键段的独占访问。它充当看门人,一次只允许一个Goroutine访问和修改受保护的资源。当一个线程持有互斥锁时,所有其他试图获取互斥锁的线程都必须等待轮到它们。
在这里插入图片描述

互斥锁提供了两个基本方法:

  • Lock() :该方法获取互斥对象,授予对资源的独占访问权。如果另一个线程已经持有互斥对象,新的线程将阻塞,直到它被释放。
  • Unlock():这个方法释放互斥锁,允许其他等待的例程获取互斥锁并访问资源。

互斥锁应用场景

对互斥锁的需求源于这样一个事实,即共享资源在被多个例程并发访问时容易受到数据竞争和不一致的影响。以下是互斥锁必不可少的一些常见场景:

  • 数据竞争

当多个协程并发地访问共享数据,并且其中至少有一个修改共享数据时,就会发生数据竞争。这可能导致不可预测的错误行为,因为无法保证执行顺序。互斥锁通过一次只允许一个协程访问共享资源来防止数据竞争。

package mainimport ("fmt""sync"
)var sharedData int
var mu sync.Mutexfunc increment() {mu.Lock()sharedData++mu.Unlock()
}func main() {var wg sync.WaitGroupfor i := 0; i < 100; i++ {wg.Add(1)go func() {defer wg.Done()increment()}()}wg.Wait()fmt.Println("Shared Data:", sharedData)
}

在这个例子中,多个协程并发地增加sharedData变量,这将导致没有互斥锁的数据竞争。

  • 临界区

临界区是访问共享资源的代码的一部分。当多个go例程试图同时访问同一个临界区时,可能会导致不可预测的行为。互斥锁确保一次只有一个程序进入临界区,保证对共享资源的有序访问。

package mainimport ("fmt""sync"
)var (sharedResource intmu             sync.Mutex
)func updateSharedResource() {mu.Lock()// Critical section: Access and modify sharedResourcesharedResource++mu.Unlock()
}func main() {var wg sync.WaitGroupfor i := 0; i < 100; i++ {wg.Add(1)go func() {defer wg.Done()updateSharedResource()}()}wg.Wait()fmt.Println("Shared Resource:", sharedResource)
}

在这个例子中,updateSharedResource 函数代表了一个关键区域,其中sharedResource被访问和修改。如果没有互斥锁,对这个临界区的并发访问可能会导致不正确的结果。

互斥锁方法

互斥锁提供了两种基本操作:锁定和解锁。让我们从理解互斥锁开始:

  • 锁定互斥对象:当一个线程想要访问共享资源或临界区时,它会调用互斥对象上的Lock()方法。如果互斥对象当前处于未锁定状态,它将被锁定,从而允许线程程继续执行。如果互斥锁已经被另一个线程锁住了,调用的线程将被阻塞,直到互斥锁可用。

下面是演示互斥锁的代码示例:

package mainimport ("fmt""sync"
)func main() {var mu sync.Mutexmu.Lock() // Lock the Mutex// Critical section: Access and modify shared resourcefmt.Println("Locked the Mutex")mu.Unlock() // Unlock the Mutex
}
  • 解锁互斥锁:当一个线程完成了它的临界区并且不再需要独占访问共享资源时,它调用互斥锁上的Unlock()方法。这个动作释放互斥锁,允许其他例程获取它。

下面是互斥锁解锁的执行方式:

package mainimport ("fmt""sync"
)func main() {var mu sync.Mutexmu.Lock() // Lock the Mutex// Critical section: Access and modify shared resourcefmt.Println("Locked the Mutex")mu.Unlock() // Unlock the Mutexfmt.Println("Unlocked the Mutex")
}

在这个例子中,在临界区之后调用mu.Unlock()来释放互斥锁,使其可供其他例程使用。

避免死锁

虽然互斥锁是确保并发安全性的强大工具,但如果使用不当,它们也会引入死锁。当两个或多个线程被卡住,等待对方释放资源时,就会发生死锁。要避免死锁,请遵循以下最佳实践:

  1. Always Unlock:确保互斥锁在锁定后处于解锁状态。如果不这样做,可能会导致死锁。
  2. 使用defer:为了保证互斥锁总是被解锁,可以考虑在函数结束时使用defer语句来解锁它们。
  3. 避免循环依赖:要小心循环依赖,在循环依赖中,多个例程会等待彼此释放资源。设计代码以避免这种情况。
package mainimport ("fmt""sync"
)func main() {var mu sync.Mutexmu.Lock() // Lock the Mutex// Critical section: Access and modify shared resource// Oops! Forgot to unlock the Mutex// mu.Unlock() // Uncomment this line to avoid deadlockfmt.Println("Locked the Mutex")// ... Some more code// Potential deadlock if mu.Unlock() is not called
}

在这个例子中,如果‘ mu.Unlock() ’行被遗忘或注释掉,则可能会发生死锁,因为互斥对象无限期地处于锁定状态。

互斥锁vs通道

互斥锁并不是Go中管理并发性的唯一工具;通道是另一种基本机制。下面是互斥锁和通道的简要比较:

  • 互斥锁用于保护临界区,并确保对共享资源的独占访问。它们适用于需要对数据访问进行细粒度控制的情况。
  • 通道用于程序间的通信和同步。它们为交换数据和同步程序提供了更高层次的抽象。

互斥锁和通道之间的选择取决于程序的具体要求。互斥锁对于需要保护共享数据的场景是理想的,而通道则适合于主要关注程序之间的通信和协调的场景。

最后总结

总之,互斥锁是确保Go中安全并发的强大工具。它们有助于保护关键区,防止数据争用,并确保共享资源的完整性。了解何时以及如何使用互斥锁对于编写既高效又可靠的并发Go程序至关重要。


http://www.ppmy.cn/ops/155258.html

相关文章

北大:三阶段学习优化多模态推理问答

&#x1f4d6;标题&#xff1a;ReasVQA: Advancing VideoQA with Imperfect Reasoning Process &#x1f310;来源&#xff1a;arXiv, 2501.13536 &#x1f31f;摘要 &#x1f538;视频问答&#xff08;VideoQA&#xff09;是一项具有挑战性的任务&#xff0c;需要理解视频中…

【深度分析】微软全球裁员计划不影响印度地区,将继续增加当地就业机会

当微软的裁员刀锋掠过全球办公室时&#xff0c;班加罗尔的键盘声却愈发密集——这场资本迁徙背后&#xff0c;藏着数字殖民时代最锋利的生存法则。 表面是跨国公司的区域战略调整&#xff0c;实则是全球人才市场的地壳运动。微软一边在硅谷裁撤年薪20万美金的高级工程师&#x…

单片机基础模块学习——DS1302时钟芯片

一、DS1302时钟简介 1.与定时器对比 DS1302时钟也称为RTC时钟(Real Time Clock,实时时钟),说到时钟,可能会想到定时器,下表来简单说明一下两者的区别。 定时器(Timer)实时时钟(RTC)精度高,可达微秒级精度较低,多为秒级计时范围短计时范围长2.开发板所在位置 下面方框里…

对神经网络基础的理解

目录 一、《python神经网络编程》 二、一些粗浅的认识 1&#xff09; 神经网络也是一种拟合 2&#xff09;神经网络不是真的大脑 3&#xff09;网络构建需要反复迭代 三、数字图像识别的实现思路 1&#xff09;建立一个神经网络类 2&#xff09;权重更新的具体实现 3&am…

因果推断与机器学习—因果推断入门(1)

在机器学习被广泛应用于对人类产生巨大影响的场景(如社交网络、电商、搜索引擎等)的今天,因果推断的重要性开始在机器学习社区的论文和演讲中被不断提及。图灵奖得主 Yoshua Bengio 在对系统 2(system 2,这个说法来自心理学家 Daniel Kahneman 的作品,人类大脑由两套系统…

Linux系统上安装与配置 MySQL( CentOS 7 )

目录 1. 下载并安装 MySQL 官方 Yum Repository 2. 启动 MySQL 并查看运行状态 3. 找到 root 用户的初始密码 4. 修改 root 用户密码 5. 设置允许远程登录 6. 在云服务器配置 MySQL 端口 7. 关闭防火墙 8. 解决密码错误的问题 前言 在 Linux 服务器上安装并配置 MySQL …

DistilBERT 是 BERT 的精简版本,具有更小、更快、更经济、更轻便的特点。

摘要 随着大规模预训练模型的迁移学习在自然语言处理&#xff08;NLP&#xff09;中变得越来越普遍&#xff0c;在边缘设备上或受限的计算训练/推理预算下运行这些大型模型仍然具有挑战性。在本研究中&#xff0c;我们提出了一种预训练较小通用语言表示模型的方法&#xff0c;…

基于微信小程序的电子竞技信息交流平台设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…