go中的map和锁
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 方法,需要传入一个匿名函数作为参数,匿名函数的
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号