Go中的map和锁
声明和初始化
只声明, var gMap map[string] string
使用var声明
声明初始化
- var hMap = map[string]string
使用make初始化
package main
import "fmt"
func main() {
var m = make(map[string]string)
m["name"] = "Wyc"
fmt.Println(m)
}
增删改查
package main
import (
"fmt"
)
func main() {
var gMap map[string]string
m2 := map[string]string{"k": "v"}
fmt.Println(gMap)
fmt.Println(m2["k"])
// 增加
m2["python"] = "Wyc"
fmt.Println(m2)
// 删除
delete(m2, "python")
fmt.Println(m2)
// 改
m2["k"] = "HAHA"
fmt.Println(m2)
// 查 单变量
selectMapKey := m2["l"]
// 双变量
_, Bl := m2["k"]
fmt.Println(selectMapKey)
if Bl {
fmt.Println("存在")
}else{
fmt.Println("不存在")
}
}
/*
结果
map[]
v
map[k:v python:Wyc]
map[k:v]
map[k:HAHA]
存在
*/
读取数据
- 在读取的时候,有两种手段,第一种单变量 lang := mapData["key"], 如果当前key不存在就附一个value类型的0值
- 第二种手段,双变量, value, bool := mapData["key"],可以根据bool来判断当前key是否存在,如果存在bool就为true,不存在则false
循环遍历map
package main
import "fmt"
func main() {
m2 := map[string]string{"name": "Wyc", "age": "23", "sex": "男"}
fmt.Println(m2)
for k, d := range m2{
fmt.Printf("key: %v, value: %v\n", k, d)
}
}
/*
结果
key: name, value: Wyc
key: age, value: 23
key: sex, value: 男
*/
key的类型:float64可以作为key吗
- bool、int、string
- 特征是支持 == 和 != 比较
- float类型可以作为key的,写入map时会做math.Float64bits()的转换,认为2.4=2.4000xxxx1,看起来时同一个key
value的类型: 任意类型
- map嵌套,每一层都需要make
package main
import "fmt"
func main() {
doubleM := make(map[string]map[string]string)
v1 := make(map[string]string)
v1["name"] = "Wyc"
doubleM["v1"] = v1
fmt.Println(doubleM)
}
go原生的map线程不安全
fatal error: concurrent map read and map write
package main
import "time"
func main() {
c := make(map[int]int)
// goruntine 写
go func() {
for i:=0 ; i < 1000; i++{
c[i] = i
}
}()
// goruntine读
go func() {
for i:=0; i < 1000; i++{
_ = c[i]
}
}()
time.Sleep(30 * time.Minute)
}
go func 是什么意思?
运行匿名goruntine函数
map线程不安全的解决办法
解决办法一、加锁
- go中的锁
- 互斥锁
- sync.mutex
- 获取到互斥锁的任务,阻塞其他任务来获取
- 意味这同一时间只能有一个任务去执行,才能持有互斥锁
- sync.mutex
package main
import (
"log"
"sync"
"time"
)
// 互斥锁
var HcMutex sync.Mutex
func runMutex(id int) {
log.Printf("[任务ID:%d]【尝试获取锁】", id)
HcMutex.Lock()
log.Printf("[任务ID:%d]【获取到了锁】", id)
time.Sleep(6 * time.Second)
HcMutex.Unlock()
log.Printf("[任务ID:%d]【工作完成 释放锁】", id)
}
func sendText() {
go runMutex(1)
go runMutex(2)
go runMutex(3)
go runMutex(4)
}
func main() {
sendText()
time.Sleep(6 * time.Minute)
}
/*
2023/02/25 17:52:18 [任务ID:2]【尝试获取锁】
2023/02/25 17:52:18 [任务ID:1]【尝试获取锁】
2023/02/25 17:52:18 [任务ID:4]【尝试获取锁】
2023/02/25 17:52:18 [任务ID:3]【尝试获取锁】
2023/02/25 17:52:18 [任务ID:2]【获取到了锁】
2023/02/25 17:52:24 [任务ID:2]【工作完成 释放锁】
2023/02/25 17:52:24 [任务ID:1]【获取到了锁】
2023/02/25 17:52:30 [任务ID:1]【工作完成 释放锁】
2023/02/25 17:52:30 [任务ID:4]【获取到了锁】
2023/02/25 17:52:36 [任务ID:4]【工作完成 释放锁】
2023/02/25 17:52:36 [任务ID:3]【获取到了锁】
2023/02/25 17:52:42 [任务ID:3]【工作完成 释放锁】
*/
- 读写锁
- 同时多个读锁任务,说明使用读写任务的读锁,可以同时施加多把读锁
- 同时多个写锁任务,说明如果并非使用读写锁的时候,退化成了互斥锁
- 西安启动写锁任务,后并大5个读锁任务,当有写锁存在时,读锁是施加不了的,写锁释放完,读锁可以施加多个
package main
import (
"log"
"sync"
"time"
)
var LockObj sync.RWMutex
func Readlock(id int) {
log.Printf("读任务id: %d, [进入写方法尝试获取读写锁]", id)
LockObj.RLock()
log.Printf("读任务id: %d, [获取到了读锁-开始干活休眠10s]", id)
time.Sleep(10 * time.Second)
LockObj.RUnlock()
log.Printf("读任务id: %d, [读任务完成-释放]", id)
}
func WriteLock(id int){
log.Printf("写任务id: %d, [进入写方法尝试获取读写锁]", id)
LockObj.Lock()
log.Printf("写任务id: %d, [获取到了写任务-开始干活休眠10s]", id)
time.Sleep(10 * time.Second)
LockObj.Unlock()
log.Printf("写任务id: %d, [写任务完成-释放]", id)
}
func read() {
for i := 0; i < 10; i++ {
go Readlock(i)
}
}
func write() {
for i := 0; i < 10; i++ {
go WriteLock(i)
}
}
// 先启动写锁
func writeFirst() {
go WriteLock(1)
time.Sleep(1 * time.Second)
go Readlock(1)
go Readlock(2)
go Readlock(3)
go Readlock(4)
go Readlock(5)
}
func readFirst() {
go Readlock(1)
go Readlock(2)
go Readlock(3)
go Readlock(4)
go Readlock(5)
time.Sleep(1 * time.Second)
go WriteLock(1)
}
func main() {
log.Println("进入程序")
writeFirst()
time.Sleep(1 * time.Hour)
}
解决办法二、使用sync.map
- go 1.9 引入内置方法,并发线程安全的map
- sync.Map 将key和value, 按照interface{}存储
- 查询出来后要类型断言 x.(int) x.(string)
- 遍历使用range 方法,需要传入一个匿名函数作为参数,匿名函数的