Golang Map并发特性
package foundation
import (
"fmt"
"strconv"
"sync"
"time"
)
/*
map 未初始化时,默认是是nil,无法直接赋值,需要先用make初始化
1. testMapInit()
map 测试被赋值后的地址变化
1. testAssign()
* map整体被赋值后,内存地址更新为新map的地址
map不是协程安全,会出现并发读写问题
1. 并发读 testReadSync() succeed 可以
2. 并发写 testWriteSync() failed 报错
3. 并发读写 testRWSync() failed 报错
4. 并发写+len() testWLenSync() succeed 可以 (任何情况下,len()操作并发安全)
5. 并发读+ rebuild testReadRebuild() succeed (读 + rebuild是不会存在并发安全问题的)
6. 并发写 + rebuild testWriteRebuild() succees (写 + rebuild也没偶并发报错,但是可能会造成写数据不生效,有逻辑异常)
map测试读写锁RWMutex(共享锁)
1. Lock/Unlock 写锁定/解锁,写的时候什么也不能干
* Lock/Unlock是互斥锁,好像和sync.Mutex中的Lock/UnLock没有区别
* testLockRead() 测试了Mutex中Lock/UnLock的互斥性
* 发现1、2、3均按顺序进入和退出
2. RLock/RUnlock 读锁定/读解锁,可以同时读,但读的同时不能写
* testRLockRead() 发现1、2、3同时进入start,表示读可以共享
3. 使用map测试
* testRWWithLock()
*/
var (
c = make(map[string]int64)
)
func mapRead() {
var i int64
for ; i < 10000; i++ {
_ = c[strconv.FormatInt(i, 10)]
}
fmt.Println("read exit")
}
func mapWrite() {
var i int64
for ; i < 10000; i++ {
c[strconv.FormatInt(i, 10)] = i
}
fmt.Println("write exit")
}
func mapRebuild() {
var i int64
for ; i < 10000; i++ {
a := make(map[string]int64)
c = a
}
fmt.Println("rebuild exit")
}
func mapLen() {
var i int64
for ; i < 10000; i++ {
_ = len(c)
}
}
// 并发读测试
func testReadSync() {
go mapRead()
go mapRead()
time.Sleep(time.Second)
}
// 并发写测试
func testWriteSync() {
go mapWrite()
go mapWrite()
time.Sleep(time.Second)
}
// 并发读写测试
func testRWSync() {
go mapRead()
go mapWrite()
time.Sleep(time.Second)
}
// 并发写 + len
func testWLenSync() {
go mapWrite()
go mapLen()
time.Sleep(time.Second)
}
// 并发读 + rebuild
func testReadRebuild() {
go mapRead()
go mapRebuild()
time.Sleep(time.Second)
fmt.Println("testReadRebuild exit")
}
// 并发写 + rebuild
func testWriteRebuild() {
go mapWrite()
go mapRebuild()
time.Sleep(time.Second)
fmt.Println("testWriteRebuild exit")
}
var lock = new(sync.Mutex)
var rwlock = new(sync.RWMutex)
// 加互斥锁 读
func LockRead(i int64) {
lock.Lock()
defer lock.Unlock()
fmt.Println("read start", i)
time.Sleep(1 * time.Second)
fmt.Println("reading", i)
fmt.Println("read done", i)
}
// 加共享锁 读 read无序,读可共享
func RLockRead(i int64) {
rwlock.RLock()
defer rwlock.RUnlock()
fmt.Println("read start", i)
time.Sleep(1 * time.Second)
fmt.Println("reading", i)
fmt.Println("read done", i)
}
func testLockRead() {
go LockRead(1)
go LockRead(2)
go LockRead(3)
time.Sleep(5 * time.Second)
}
func testRLockRead() {
go RLockRead(1)
go RLockRead(2)
go RLockRead(3)
time.Sleep(5 * time.Second)
}
// map 加读锁read
func mapRLockRead() {
var i int64
for ; i < 10000; i++ {
rwlock.RLock()
fmt.Println("read", i)
_ = c[strconv.FormatInt(i, 10)]
rwlock.RUnlock()
}
}
// map 加互斥锁write
func mapLockWrite() {
var i int64
for ; i < 10000; i++ {
rwlock.Lock()
fmt.Println("write", i)
c[strconv.FormatInt(i, 10)] = i
rwlock.Unlock()
}
}
func testRWWithLock() {
go mapRLockRead()
go mapRLockRead()
go mapLockWrite()
time.Sleep(time.Second)
}
// 测试map 被赋值后的内存地址变化
func testAssign() {
a := map[string]int64{"a": 1}
fmt.Println(a, &a)
fmt.Printf("%p\n", &a)
b := map[string]int64{"b": 1}
fmt.Println(b, &b)
fmt.Printf("%p\n", &b)
a = b
fmt.Println(a, &a)
fmt.Printf("%p\n", &a)
}
func testMapInit() {
// 方式一:先声明,在初始化
var mapA map[int64]string // 仅声明map时,其默认值是nil,不能直接赋值,需要make初始化
// mapA[12] = "test" // panic: assignment to entry in nil map
mapA = make(map[int64]string)
mapA[12] = "test"
fmt.Println(mapA)
// 方式二:直接初始化赋值
mapB := make(map[int64]string)
mapB[12] = "test1"
fmt.Println(mapB)
// 方式三:声明 + 初始化
var mapC map[int64]string = map[int64]string{12: "test2", 13: "test3"}
fmt.Println(mapC)
}
func TestMap() {
// testWriteSync() // fatal error: concurrent map writes
// testReadSync() // success
// testRWSync() // fatal error: concurrent map read and map write
// testReadRebuild() // success
// testWriteRebuild() // success
// testLockRead() // 1、2、3依次start/reading/done
// testRLockRead() // 1、2、3同时进入start
// testRWWithLock() // 加锁后读写无error,但是不能体现读锁的特性
// testWLenSync() // len() 操作和write无并发错误
// testAssign()
// testMapInit()
}