Mutex 如何解决资源并发访问问题?

本文介绍了Go语言中并发编程遇到的问题,如秒杀系统的超卖、计数器更新错误等,这些都源于并发访问共享资源。为了解决这类问题,文章详细讲解了互斥锁(Mutex)的工作原理和使用方法,通过示例展示了如何使用Mutex避免数据竞争。同时,提到了Go的race检测工具来检测并发问题。最后,给出了一个使用Mutex修复并发问题的示例代码。
摘要由CSDN通过智能技术生成

并发问题介绍

在实际项目中,并发是非常常见的,尤其今天电商的兴起如购物时的秒杀系统、秒杀出现超卖、计数器、多个 Goroutine 并发更新同一个资源、用户账户出现透支、buffer 中数据混乱等等问题。

这些问题如何去解决?对,用互斥锁,在 Go 语言中,就是 Mutex。

互斥锁实现机制

首先知道什么是临界资源,是指一个被共享的资源或者一个整体的一组共享资源。比如对数据库的访问、对某共享结构的操作、对一个I/O设备的使用、对一个连接池中的连接调用等等。

对临界区限定同时只能有一个线程持有锁,其他线程如果想访问就会失败,或在外等待,直到持有线程退出临界区、其他线程中某个线程才有机会接待持有这个临界区。

Mutex 基本使用方法

Mutex 是 Go 的标准库中的 sync 包中所提供的一个同步原语,这个包还定义了一个 Lock 接口,而 Mutex 就是实现了这个接口的。

通过进入源码,你会发现互斥锁 Mutex 就是提供两个方法 Lock 和 Unlock,当你进入 临界区之前调用 Lock 方法,退出临界区时调用 Unlock方法。

当一个 goroutine 通过调用 Lock 方法获得了这个锁的拥有权后, 其它请求锁的 goroutine 就会阻塞在 Lock 方法的调用上,直到锁被释放并且自己获取到了这个锁的拥有权。

例子:创建了 10 个 goroutine,同时不断地对一个变量(count)进行加 1 操作,每个 goroutine 负责执行 10 万次的加 1 操作,我们期望的最后计数的结果是 1000000 (一百万)。

func main() {
	var count int = 0
	var wg sync.WaitGroup
	wg.Add(10)
	for i:=0;i<10;i++{
		go func() {
			defer wg.Done()
			for j:=0;j<100000;j++{
				count++
			}
		}()
	}
	wg.Wait()
	fmt.Println("Count=",count)
}

这里使用 sync.WaitGroup 来等待所有的 goroutine 执行完毕后,再输出最终的结果。上面代码运行结果,不管你运气再好,总是没有输出 一百万

为什么?

这就是并发问题了,count++ 不是原子操作,它至少包括几个步骤,如读取变量 count 的当前值,对这个值加1,把结果再保存到 count 中,因为不是原子操作,可能就出现并发问题。

Go 提供了一个检测并发访问共享资源是否有问题的工具,使用 race 参数:go run -race main.go ,这样就会打印出警告信息。

这个警告不但会告诉你有并发问题,而且还会告诉你哪个 goroutine 在哪一行对哪个变量有写操作。该工具只能通过真正对实际地址进行读写访问时才能探测,并不能再编译的时候发现 data race 的问题。

运行 go tool compile -race -S mutexTest.go ,重点关注一下 count++ 前后的编译后 的代码:

以上问题,共享资源是 count 变量,临界区是 count++ ,只要在临界区前面获取锁,离开临界区时候释放锁,就可以解决 data race 问题了,代码如下:

func main() {
	var count int = 0
	var mu sync.Mutex
	var wg sync.WaitGroup
	wg.Add(10)
	for i:=0;i<10;i++{
		go func() {
			defer wg.Done()
			for j:=0;j<100000;j++{
				mu.Lock()
				count++
				mu.Unlock()
			}
		}()
	}
	wg.Wait()
	fmt.Println("Count=",count)
}

Mutex 其他用法

很多情况下,Mutex 会嵌入到其它 struct 中使用,比如下面的方式:

type Counter struct { 
mu sync.Mutex 
Count uint64 
}

更多资料收录于GitHub:https://github.com/metashops/GoFamily
欢迎去获取。。。

在这里插入图片描述
Java推荐
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值