在业务当中我们常常需要遇到并发读写,其中读的频率远大于写的频率的情况,对数据的写入是需要互斥的,也就是同时只能允许一个线程去修改某个数据,但是允许多个读线程同时去读取某个数据。总的来说就是:
- 当有一个写线程来修改某个临界量的时候,不允许其它的写线程和读线程来进入临界区;
- 当有一个读线程来读取某个临界量的时候,不允许其它的写线程进入临界区,但允许其它的读线程来进入临界量;
对比互斥锁和读写锁
当使用sync.Mutex
来实现并发同步
使用sync.Mutex
来对读线程和写线程来进行互斥操作,代码如下:
package main
import (
"fmt"
"sync"
"time"
)
var (
wg sync.WaitGroup
lock sync.Mutex
data int64
)
func read() {
defer lock.Unlock()
defer wg.Done()
lock.Lock()
time.Sleep(time.Millisecond) //模拟读取耗时
}
func write() {
defer lock.Unlock()
defer wg.Done()
lock.Lock()
data++
}
func main() {
start := time.Now()
for i := 0; i < 1000; i++ {
wg.Add(1)
go read()
}
for i := 0; i < 10; i++ {
wg.Add(1)
go write()
}
wg.Wait()
fmt.Println(data)
d := time.Now().Sub(start)
fmt.Println(d)
}
结果输出如下:
容易看出,耗时
1.5s
多
当使用sync.RWMutex
来实现并发同步
使用sync.RWMutex
来对读线程和写线程来进行互斥操作,代码如下:
package main
import (
"fmt"
"sync"
"time"
)
var (
wg sync.WaitGroup
lock sync.RWMutex
data int64
)
func read() {
defer lock.RUnlock()
defer wg.Done()
lock.RLock()
time.Sleep(time.Millisecond) //模拟读取耗时
}
func write() {
defer lock.Unlock()
defer wg.Done()
lock.Lock()
data++
}
func main() {
start := time.Now()
for i := 0; i < 1000; i++ {
wg.Add(1)
go read()
}
for i := 0; i < 10; i++ {
wg.Add(1)
go write()
}
wg.Wait()
fmt.Println(data)
d := time.Now().Sub(start)
fmt.Println(d)
}
结果输出如下:
读写锁
容易看出,耗时7ms
多
总结
方式 | sync.Mutex | sync.RWMutex |
---|---|---|
耗时 | 1.5s | 7ms |
所以在遇到并发读写,并且读的频率远大于写的频率的时候,需要使用sync.RWMutex
读写锁来实现读写并发。
网友评论