首页 文章 Go语言基础 golang中的map以及并发问题
0
0
0
17

golang中的map以及并发问题

map集合 golang 问题 map

特点

  1. 无序的键值对集合,使用key来获取,无法通过index来获取,返回顺序未知,因此每次打印的顺序可能不一样。
  2. 可迭代 for range。
  3. 使用hash表实现的,是引用类型。
  4. len()获取长度。
  5. key可以是所有可比较的类型:boolean, numeric, string, pointer, channel, interface, array, struct。
  6. 非并发安全的。

声明与初始化

var m1 map[string]int // 只是声明,依然是 nil
m2 := make(map[string]int) // 声明并创建,也就是说已经分配地址了
m3 := map[string]int{"golang":90, "python":90}
m4 := map[string]int{}

如果只声明而不初始化map,那么就会创建一个nil map。将不能用来存放键值对,比如上面的m1。

if m1 == nil {
	// 空的 map
	m1 = make(map[string]int)
}

通过key访问value,访问一个不存在的key,会返回value类型的零值,而不会报错。
因此,无法通过值来判断key是否存在,需要通过ok-idiom的方式

value, ok := map[key]

if ok == true {
	// key是存在的
} else {
	// key是不存在的
}

设置值

m2[k1] = 100

如果k1不存在就是新增

删除某个key

delete(m2, k1)

k1 不存在也不会报错,并不会真的删除,只是做了一个标记而已。

遍历map

for key, value := range m2 {
	// 顺序不是固定的
}

实现按一定顺序遍历map

把key单独抽取出来,放在数组中,对数组进行排序,然后遍历数组即可。

map的并发问题

func testmap3() {
	m := map[int]int{}

	for i := 0; i < 21; i++ {
		go func() {
			m[1] = 1
		}()
	}
}

报错:fatal error: concurrent map writes

两种初始化方式的对比

var wg sync.WaitGroup
var Dog map[int]int

func main() {
	num := 10
	wg.Add(num)

	for i := 0; i < num; i++ {
		go func() {
			defer wg.Done()
			add()
		}()
	}
	wg.Wait()
}

func add() {
	if Dog == nil {
        
         /*
			方法1
			Dog = map[int]int{
				1:  setV(),
				2:  setV(),
				3:  setV(),
				4:  setV(),
				5:  setV(),
				6:  setV(),
				7:  setV(),
				8:  setV(),
				9:  setV(),
				10: setV(),
			}
		*/
        
         // 方法2
		Dog = make(map[int]int)
		Dog[1] = setV()
		Dog[2] = setV()
		Dog[3] = setV()
		Dog[4] = setV()
		Dog[5] = setV()
		Dog[6] = setV()
		Dog[7] = setV()
		Dog[8] = setV()
		Dog[9] = setV()
		Dog[10] = setV()
	} else {
		fmt.Println(Dog[10])
	}
}

func setV() int {
	return rand.Intn(10000)
}

方法1 实际上是先初始化一个临时变量,最后将其赋值给 Dog,即使出现了并发,也只会是后者覆盖前者。但是方法2 会出现问题:

  1. concurrent map read and map write
  2. Dog[10] 可能还没有被赋值。
  3. Dog 被分配了多次内存

也就说map的并发写是会报错的,所以需要规避这个问题。

内置的并发安全的map数据结构
sync.Map{}
	func (m *Map) Delete(key interface{})
    func (m *Map) Load(key interface{}) (value interface{}, ok bool)
    func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool)
    func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)
    func (m *Map) Range(f func(key, value interface{}) bool)
    func (m *Map) Store(key, value interface{})

通过加锁
sync.Mutex
sync.RWMutex
sync.Once

map的GC回收机制

map是只增不减的一种数组结构,那些被 delete 的 key 会被打上标记,但是不会被GC回收。对于大的map对象。如果不再使用了最好使用赋值 nil 的方式使其整个的可以被GC。

到此这篇关于“golang中的map以及并发问题”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持Go语言编程网!

相关文章

创建博客

开始创作
写作能提升自己能力,也能为他人分享知识。

在线教程

查看更多
  • Go入门指南

    Go入门指南

  • Go语言高级编程

    Go语言高级编程

  • Go Web 编程

    Go Web 编程

  • GO专家编程

    GO专家编程

  • Go语言四十二章经

    Go语言四十二章经

  • 数据结构和算法(Golang实现)

    数据结构和算法(Golang实现)

Go语言编程网

微信扫码关注订阅号


博客 资讯 教程 我的