etcd 访问 锁_Golang 临界资源安全与互斥锁

(给Go开发大全加星标)

来源:五更灯火

https://blog.csdn.net/qq_44720314/article/details/105125185

【导读】本文通过详细例子,介绍了 Go 语言并发编程中对临界资源的管理和互斥锁的使用。

在任何支持并发编程的语言中,进程/线程对临界资源的竞争都是不可避免的问题,举一个例子来引入

package mainimport (  "fmt"  "math/rand"  "time")//全局变量票数var tickets = 10func main(){    //三个goroutine  模拟售票窗口  go saleTickets("售票口1")  go saleTickets("售票口2")  go saleTickets("售票口3")    //为了保证3个goroutine协程正常工作,先将主线程睡眠5秒  time.Sleep(5*time.Second)}func saleTickets(name string){  //随机数种子  rand.Seed(time.Now().UnixNano())  for {    if tickets >0{      //随机睡眠1~1000ms      time.Sleep(time.Duration(rand.Intn(1000))*time.Millisecond)      fmt.Println(name,"余票:",tickets)      tickets--    }else{        fmt.Println(name,"售罄,已无票。。")        break    }  }}

在以上的代码中,使用三个并发运行的go协程模拟了三个售票窗口同时售票。而由于全局变量tickets会被三个协程在一段时间内同时访问, 因此tickets就是我们所说的“临界资源”。

我们可以发现:

控制台输出结果:

售票口1 余票:10售票口3 余票:10售票口2 余票:10售票口1 余票:7售票口2 余票:6售票口2 余票:5售票口1 余票:4售票口3 余票:3售票口2 余票:2售票口1 余票:1售票口1 售罄,已无票。。售票口2 余票:0售票口2 售罄,已无票。。售票口3 余票:-1售票口3 售罄,已无票。。
  • 在开始时,三个窗口同时读到信息:tickets=10,从而随机都输出了余票=10

  • 而在结尾时,竟然出现了余票为负数的情况,其产生的原因在于,票数快要卖完时,当售票口1余票1,并且售完这一张票后,在这个时间段内,售票口2已经进入了if tickets > 0满足条件的代码块内,然而售票口1此时将最后一张票售出,tickets 由1变为0售票口2打印出来了不应该出现的结果:余票0,同理售票口3打印了不该出现的结果:余票-1

这就产生了临界资源竞争的问题,产生了错误的程序结果

临界资源安全问题的解决

要想解决临界资源安全的问题,很多编程语言的解决方案都是同步。通过上锁的方式,某一时间段内,只能允许一个goroutine来访问这个共享数据,当前goroutine访问完毕,解锁后,其他的goroutine才能访问。

通过go语言,我们可以利用sync包下的锁操作,包含互斥锁和读写锁。


以下代码使用了互斥锁来实现售票窗口之间的同步:

package mainimport (  "fmt"  "math/rand"  "sync"  "time")//全局变量票数var tickets = 10var mutex sync.Mutexfunc main(){  //三个goroutine  模拟售票窗口  go saleTickets("售票口1")  go saleTickets("售票口2")  go saleTickets("售票口3")  //主协程睡眠,要保证睡眠时间大于子协程一共的执行时间,否则子协程无法正常执行完毕  time.Sleep(10*time.Second)}func saleTickets(name string){  //随机数种子  rand.Seed(time.Now().UnixNano())  for {    //上锁    mutex.Lock()    if tickets >0{      //随机睡眠1~1000ms      time.Sleep(time.Duration(rand.Intn(1000))*time.Millisecond)      fmt.Println(name,"余票:",tickets)      tickets--    }else{      mutex.Unlock()      fmt.Println(name,"售罄,已无票。。")      break    }    //解锁    mutex.Unlock()  }}

Go并发编程

在Go的并发编程中有一句很经典的话:不要以共享内存的方式去通信,而要以通信的方式去共享内存。

在Go语言中并不鼓励用锁保护共享状态的方式在不同的Goroutine中分享信息(以共享内存的方式去通信)。而是鼓励通过channel将共享状态或共享状态的变化在各个Goroutine之间传递(以通信的方式去共享内存),这样同样能像用锁一样保证在同一的时间只有一个Goroutine访问共享状态。


当然,在主流的编程语言中为了保证多线程之间共享数据安全性和一致性,都会提供一套基本的同步

工具集,如锁,条件变量,原子操作等等。Go语言标准库也毫不意外的提供了这些同步机制,使用方式也和其他语言差不多

 - EOF -

推荐阅读(点击标题可打开)

1、Golang 防内存泄漏编码原则

2、用etcd做go-micro的服务发现

3、k8s调度的优先级及抢占机制源码分析

如果觉得本文不错,欢迎转发推荐给更多人。

5a0dd6cc4b9fb0ef1bbacd9e0f2a1b62.png

分享、点赞和在看

支持我们分享更多好文章,谢谢!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值