1. 并发安全与锁
当多个goroutine
同时操作有个资源的时候容易出现数据不安全(竟态问题)
。
举个栗子
package main
import (
"fmt"
"sync"
)
var n int //定义一个整数,初始值为0
var wg sync.WaitGroup
func add() {
defer wg.Done()
for i := 0; i < 1000000; i++ {
n += 1
}
}
func main() {
wg.Add(3)
go add()
go add()
go add()
wg.Wait()
fmt.Println(n)
}
每次出现的结果不一样,出现竟态问题!
2. 互斥锁
如何解决数据竟态问题?
在同一时刻只能有一个groutine
能对数据进行修改
Go语言中使用sync
包中的Mutex
类型来实现互斥锁。
示例如下:
package main
import (
"fmt"
"sync"
)
var n int //定义一个整数,初始值为0
var wg sync.WaitGroup
var lock sync.Mutex
func add() {
defer wg.Done()
for i := 0; i < 1000000; i++ {
lock.Lock() // 加锁
n += 1
lock.Unlock() // 解锁
}
}
func main() {
wg.Add(3)
go add()
go add()
go add()
wg.Wait()
fmt.Println(n)
}
加上锁以后同一时刻只能由一个goroutine
修改数据,保证了数据安全,但是降低了效率
3.读写互斥锁
学习读写互斥锁之前明白一个小概念,我们对数据的操作无非两种,一种是读操作,一种是写操作。
在工作中读操作的频率是远远大于写操作的。
在之前使用的互斥锁是完全锁,部分读写都加上锁,Go语言提供的一种优化方法。对加锁进行读写分离。不限制读操作,只限制写操作。
读写互斥锁在Go语言中使用sync
包中的RWMutex
类型。
package main
import (
"fmt"
"sync"
"time"
)
var n int
var wg sync.WaitGroup
// var lock sync.Mutex
var rwlock sync.RWMutex
func read() {
defer wg.Done()
// lock.Lock() // 加互斥锁
rwlock.RLock() // 加读锁
time.Sleep(time.Millisecond)
// lock.Unlock() // 释放互斥锁
rwlock.RUnlock() // 释放读锁
}
func write() {
defer wg.Done()
// lock.Lock()
rwlock.Lock() // 加写锁
n += 1
time.Sleep(time.Millisecond * 50)
// lock.Unlock()
rwlock.Unlock() // 释放写锁
}
func main() {
start := time.Now()
for i := 0; i < 10000; i++ {
go read()
wg.Add(1)
}
for i := 0; i < 10; i++ {
go write()
wg.Add(1)
}
wg.Wait()
now := time.Now().Sub(start)
fmt.Println(now)
}
读写互斥锁适合读多写少的场景,如果读写频率相差不打,读写锁的优势很难发挥出来