5000字带你精通golang sync包的所有姿势

golang是一门支持并发编程的语言,它提供了goroutine和channel等机制来实现多个任务的并行执行。但是,并发编程也会带来一些挑战,比如数据竞争、死锁、内存泄漏等。为了解决这些问题,golang提供了一个标准库sync,它包含了一些高性能的同步原语,可以帮助我们更好地管理并发状态和资源。

一、sync.Mutex

相信大多数同学都有线上抢购东西的经历,在开始抢购的一瞬间,有大量的用户都发起了请求,形成了不同的线程,对同一个商品进行抢购。现在我们来模拟一下这个场景,假设待抢购的商品是一款网红电视机,库存为1000台,在开始抢购的一瞬间,有刚好1000人点击了购买按钮,按照预期,抢购完成后,库存为0,代码如下:

func main() {
   
	stock := 1000
	group := sync.WaitGroup{
   }
	group.Add(1000)
	for i := 0; i < 1000; i++ {
   
		go func() {
   
			stock -= 1
			group.Done()
		}()
	}
	group.Wait()
	fmt.Println(stock)
}

输出如下:

76

可能不熟悉并发编程的同学可能会想:咦?为啥不是0呢?归根到底,-=1这个操作并不是原子性的,为了解决这个问题,go引入了sync.Mutex{},这是一个互斥锁,它可以保证在任意时刻,只有一个goroutine可以访问某个共享变量或临界区。我们可以使用Lock()和Unlock()方法来加锁和解锁。我们对上面的代码做如下的改造例如:

func main() {
   
	stock := 1000
	mutex := sync.Mutex{
   } //1.声明互斥锁
	group := sync.WaitGroup{
   } 
	group.Add(1000)
	for i := 0; i < 1000; i++ {
   
		go func() {
   
			mutex.Lock() //2.加锁
			stock -= 1
			mutex.Unlock() //3.解锁
			group.Done()
		}()
	}
	group.Wait()
	fmt.Println(stock)
}

输出如下:

0

为了保证stock的正确性,我们使用了sync.Mutex{}来加锁和解锁,这样同时只会有一个协程对stock变量进行操作,这样就可以避免数据竞争的问题,最终输出结果也符合我们最终的预期。

二、sync.RWMutex

sync.Mutext解决了并发问题,但是在实际使用场景中,有很多时候读的次数是远大于写的次数的,读取数据并不会对数据造成影响,只需要限制其他协程不能对数据同时进行修改即可,不需要限制其他的协程对该数据的读取操作。sync.RWMutex{}是一个读写锁,它可以保证在任意时刻,只有一个

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值