美文网首页
go map实现

go map实现

作者: lucasgao | 来源:发表于2021-03-15 23:48 被阅读0次

Q

  1. 怎么平滑的扩容

冲突解决的2种方法

开放寻址法

open-addressing-and-get

开放寻址中对性能影响最大的计算装载因子。

随着装载因子的怎额更多,线性探测的时间就会逐渐增加。如果装载因子到1,那么查找和插入的复杂度都会退化到O(n)。

拉链法

separate-chaing-and-get

在一个性能比较好的哈希表中,每一个桶中都应该有01个元素,有时会有23个。再多性能就会比较差了。

名词

装载因子

它是数组中元素的数量与数组大小的比值。

并发写问题

fatal error: concurrent map writes

问题出现的原因

go中的map不是并发安全的,所以当多个goroutine同时对map执行写操作的时候,就会报刚刚的错误。

示例代码

package main

import (
    "math/rand"
    "time"
)

func init() {
    rand.Seed(time.Now().UnixNano())
}

func main() {
    ConcurrentWrite()
}

func ConcurrentWrite() {
    m := make(map[int]int)

    f := func() {
        k, v := rand.Int()%50, rand.Int()%1000
        m[k] = v
    }
    for i := 0; i < 10; i++ {
        go f()
    }
    time.Sleep(time.Second * 5)
}

上面代码里,我们启动了10个协程对同一个map执行写操作,强制触发并发写。

运行结果

fatal error: concurrent map writes
fatal error: concurrent map writes

go是如何检测到对map的并发写的

那么我们知道发生的原因之后,追本溯源,我们一起来看下go是如果检测到并进行报错的

首先对map的写在源码上映射为 mapassign函数

// Like mapaccess, but allocates a slot for the key if it is not present in the map.
func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer{
 ...
}

因为在这个函数里面执行写操作,那么panic也是在这里报出来的了。

我们忽略到无关的代码,先force在 concurrent map writes 报错。我们发现有如下代码

...
if h.flags&hashWriting != 0 { // h是指向map,
        throw("concurrent map writes")
}
...
// Set hashWriting after calling t.hasher, since t.hasher may panic,
// in which case we have not actually done a write.
h.flags ^= hashWriting // 位操作,把hasWriting 标志位记为1
...
// do wirte things
...
if h.flags&hashWriting == 0 {
  throw("concurrent map writes")
}
h.flags &^= hashWriting // 清空标志位


可以看到,go是通过标志位实现的。在写之前,先吧标志位置为1,写之后,改为0。并且每次修改之前都会做判断,如果不符合预期,则会报错。

如何避免

  1. 尽量少定义全局map变量
  2. 如果必须定义全局map变量,可以加锁
    1. 优化1.可以采用cow策略,read不加锁,每次修改copy修改,再赋值
    2. 优化2.可以采用分片锁,减小锁的粒度
  3. 使用sync.Map

相关文章

  • Golang之Map源码

    引用 深入 Go 的 Map 使用和实现原理 哈希表 深度解密Go语言之map Golang map 的底层实现 使用

  • 第九章:Go语言映射类型map

    1. map概述 Go语言中map字典类型时散列表(hash table)的实现,因为Go语言中将map中的每个键...

  • 10.map

    Go语言基础之map | Golang Go语言中提供的映射关系容器为map,其内部使用散列表(hash)实现。 ...

  • go map实现

    Q 怎么平滑的扩容 冲突解决的2种方法 开放寻址法 开放寻址中对性能影响最大的计算装载因子。 随着装载因子的怎额更...

  • 第03天(复合类型)_map的基本使用

    24_map的基本使用.go 25_map赋值.go 26_map遍历.go 27_map删除.go 28_map...

  • Go语言高并发Map解决方案

    Go语言高并发Map解决方案 Go语言基础库中的map不是并发安全的,不过基于读写锁可以实现线程安全;不过在Go1...

  • Go学习笔记四(Map扩展)

    Map 与⼯厂模式 Map 的 value 可以是一个⽅法 实现Set Go 的内置集合中没有 Set 实现, 可...

  • 深度解密Go语言之map(1)

    map 的底层如何实现 首先声明我用的 Go 版本:go version go1.9.2 darwin/amd64...

  • go和python的深浅拷贝理解

    go深拷贝, 就是拷贝值 go浅拷贝, 拷贝引用 go中赋值就能实现拷贝,针对引用类型(slice,map,cha...

  • 【go笔记】map结构的简介

    go的hashtable是用map实现。今天简单的整理了map的结构。 1.map的结构 hmap 在源码中, m...

网友评论

      本文标题:go map实现

      本文链接:https://www.haomeiwen.com/subject/mvqrcltx.html