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
      • 获取到互斥锁的任务,阻塞其他任务来获取
      • 意味这同一时间只能有一个任务去执行,才能持有互斥锁

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 方法,需要传入一个匿名函数作为参数,匿名函数的
posted @ 2023-02-26 10:22  WrYcF  阅读(133)  评论(0编辑  收藏  举报
Live2D