您的位置 首页 golang

golang基于共享变量的并发

文章参考go语言圣经,并进行了整理,希望可以帮到工作中有需要的小伙伴~

在一个线性(只有一个goroutine)程序中,程序的执行顺序由程序的逻辑来决定。在有两个或者更多goroutine的程序中,每一个goroutine内的语句也是按照顺序去执行的,但是没法知道不同goroutine中事件的执行顺序,当无法确认一个事件是在另一个事件前面或后面发生的时候,说明这些事件是并发的

1、sync.Mutex互斥锁

在并发模型中,可以用互斥锁来处理并发问题,锁本身可以认为是一个共享变量,一个线程加了互斥锁后,其他线程只能等待,直到锁被释放才能获取新锁。在go语言中sync包里的Mutex类型直接支持互斥锁,Lock方法可以获取锁,Unlock方法会释放锁

 // 示例
var (
balance int
mu      sync.Mutex
wg      sync.WaitGroup
)
func Deposit(amount int) {
mu.Lock() // 加锁
balance = balance + amount
fmt.Println("存入金额:" + strconv.Itoa(amount))
mu.Unlock() // 释放锁
// 在goroutine完成任务后,调用wg.Done(),等同于wg.Add(-1)
wg.Done()
}
func Balance() int {
mu.Lock()
// 释放资源
defer mu.Unlock()
return balance
}
func main() {
wg.Add(3)
// 启动3个goroutine
go Deposit(100)
go Deposit(200)
go Deposit(300)
// 阻塞主线程,等待所有goroutine完成调用
// 当所有goroutine都调用完wg.Done()之后才返回
wg.Wait()
fmt.Println(Balance())
}
// 输出
存入金额:300
存入金额:100
存入金额:200
账户总额:60总结1> Mutex 为互斥锁,Lock() 加锁,Unlock() 解锁2> 在一个 goroutine 获得 Mutex 后,其它 goroutine 只能等待这个 goroutine 释放该 Mutex3> 使用 Lock() 加锁后,不能再继续对其加锁,通过 Unlock() 解锁后才能再次加锁4> 在 Lock() 之前使用 Unlock() 会导致 panic 异常5> 在同一个 goroutine 中的 Mutex 解锁之前再次进行加锁,会导致死锁6> 常用于读写不确定,并且只有一个读或者写的场景  
 // 示例
func main() {
    var mutex sync.Mutex // 互斥锁
mutex.Lock()
fmt.Println("Locked")
c := make([]chan int, 4)
for i := 0; i < 3; i++ {
c[i] = make(chan int)
go func(i int, c chan int) {
fmt.Println("Start: ", i)
mutex.Lock()
fmt.Println("Goroutine Locked: ", i)
time.Sleep(time.Second)
fmt.Println("Goroutine Unlock: ", i)
mutex.Unlock()
c <- i
}(i, c[i])
}
time.Sleep(time.Second)
fmt.Println("Unlock")
mutex.Unlock()
time.Sleep(time.Second)
for _, c := range channels {
<-c
}
}  

2、sync.RWMutex读写锁

 // 示例
var (
balance = 10
//mu      sync.Mutex // 互斥锁
mu sync.RWMutex // 读写锁
wg sync.WaitGroup
)
func Deposit() {
if Balance() > 0 {
mu.Lock()
if balance > 0 {
balance--
fmt.Println("当前余额:" + strconv.Itoa(balance))
} else {
fmt.Println("余额不足...")
}
mu.Unlock()
} else {
fmt.Println("余额不足.")
}
wg.Done()
}
func Balance() int {
// 注:可分别通过方式1和方式2,观察程序运行的差异~

// 方式1
// 使用互斥锁(Mutex),读取的时候会阻塞其它goroutine运行
mu.Lock()
defer mu.Unlock()
// 方式2
// 使用读写锁(RWMutex),多个goroutine可以同时读取
//mu.RLock()
//defer mu.RUnlock()
return balance
}
func main() {
for i := 0; i < 20; i++ {
wg.Add(1)
go Deposit()
}
wg.Wait()
}  

1> RWMutex 是单写多读锁,该锁可以加多个读锁或者一个写锁
2> 读锁占用的情况下会阻止写,不会阻止读,多个 goroutine 可以同时获取读锁
3> 写锁会阻止其他 goroutine 读写,整个锁由该 goroutine 独占
4> 多用于读多写少的场景

#Lock() 和 Unlock()
在加写锁之前已经有其它的读锁和写锁,则 Lock() 会阻塞直到该锁可用

 // 读写锁示例1
func main() {
    var mutex sync.RWMutex // 读写锁
mutex.Lock()
fmt.Println("Locked")
c := make([]chan int, 4)
for i := 0; i < 3; i++ {
c[i] = make(chan int)
go func(i int, c chan int) {
fmt.Println("Start: ", i)
mutex.Lock() // Lock() 
fmt.Println("Goroutine Locked: ", i)
time.Sleep(time.Second)
fmt.Println("Goroutine Unlock: ", i)
mutex.Unlock() // Unlock()
c <- i
}(i, c[i])
}
time.Sleep(time.Second)
fmt.Println("Unlock")
mutex.Unlock()
time.Sleep(time.Second)
for _, c := range c {
<-c
}
}
// 输出
Locked
Start:  3
Start:  2
Start:  1
Start:  0
Unlock
Goroutine Locked:  3
Goroutine Unlock:  3
Goroutine Locked:  2
Goroutine Unlock:  2
Goroutine Locked:  1
Goroutine Unlock:  1
Goroutine Locked:  0
Goroutine Unlock:  0
  

#RLock() 和 RUnlock()
RLock() 加读锁时,如果存在写锁,则无法加读锁;当只有读锁或者没有锁时,可以加读锁,读锁可以加多个

 // 读写锁示例2
func main() {
    var mutex sync.RWMutex // 读写锁
mutex.Lock()
fmt.Println("Locked")
c := make([]chan int, 4)
for i := 0; i < 3; i++ {
c[i] = make(chan int)
go func(i int, c chan int) {
fmt.Println("Start: ", i)
mutex.RLock() // RLock()
fmt.Println("Goroutine Locked: ", i)
fmt.Println("Goroutine Unlock: ", i)
mutex.RUnlock() // RUnlock()
c <- i
}(i, c[i])
}
time.Sleep(time.Second)
fmt.Println("Unlock")
mutex.Unlock()
time.Sleep(time.Second)
for _, c := range c {
<-c
}
}
// 输出
Locked
Start:  0
Start:  2
Start:  3
Start:  1
Unlock
Goroutine Locked:  1
Goroutine Unlock:  1
Goroutine Locked:  2
Goroutine Unlock:  2
Goroutine Locked:  3
Goroutine Locked:  0
Goroutine Unlock:  0
Goroutine Unlock:  3  

#有兴趣的小伙伴可以将读写锁示例1和示例2运行一下,对比打印出来数据的差别,可以有助于理解锁之间的差异~

文章来源:智云一二三科技

文章标题:golang基于共享变量的并发

文章地址:https://www.zhihuclub.com/98238.shtml

关于作者: 智云科技

热门文章

网站地图