一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第22天,点击查看活动详情。
问题
每次读写共享资源都要加锁的话,性能会十分低下。由于一般情况下写的时候不能同时读、读的时候不能同时写,但是读的时候可以同时读,所以使用读写锁可以显著提高性能。
读写锁
var (
sum int
mutex sync.RWMutex
)
func readSum() int {
mutex.RLock()
defer mutex.RUnlock()
b := sum
return b
}
通过只获取读锁,各个协程可以同时去读取数据,不需要等待一个协程读完再读,可以大大提高性能。
sync.WaitGroup
在一些代码中我们会有下面的代码:
time.Sleep(2 * time.Second)
这是让主函数等待两秒再返回,以免主函数返回退出后一些协程还没有执行完毕。但是有两个问题:
- 如果协程在两秒内执行完毕,主函数本该提前返回却等待两秒后再反回会造成性能问题
- 如果协程在两秒内仍未执行完毕,主函数返回退出,程序出现错误
Go语言为我们提供sync.WaitGroup,功能是一旦协程全部执行完毕,程序马上退出,这样可以解决上面的两个问题。
var (
sum int
mutex sync.Mutex
rwMutex sync.RWMutex
)
func main() {
run()
}
func run() {
var wg sync.WaitGroup
wg.Add(110)
for i := 0; i < 100; i++ {
go func() {
defer wg.Done()
add(10)
}()
}
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
fmt.Println("sum:", readSum())
}()
}
wg.Wait()
}
func add(i int) {
sum += i
}
func readSum() int {
rwMutex.RLock()
defer rwMutex.RUnlock()
b := sum
return b
}
首先通过通过 wg.Add(110)将计数器置为协程的数量,执行完一个协程时通过defer wg.Done()将计数器值减一,通过wg.Wait()一直等待,知道计数器值为0,这时候所有的协程都执行完毕了。以上代码可以做到当110个协程执行完毕的时候程序退出。
sync.WaitGroup适合协调多个协程共同做同一个事情的场景。