侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

在Go语言中sync.Map和map的区别浅析

Go  /  管理员 发布于 2年前   729

在Go中,映射通常用于存储键值对,然而,当谈到并发编程时,我们需要小心访问共享数据以避免竞争条件。为了解决这个问题,Go 提供了sync.Map类型,它是 map 的一种安全并发的替代方案。

在本文中,我们将讨论sync.Map和Go 中的map之间的区别,以及如何在不同的场景中使用它们。


Go中的maps

map是一个无序的键值对的集合,其中的键是唯一的,值可以是任何类型。

map在Go中常用于存储数据,可以用make函数来创建,下面是代码:

m := make(map[string]int)

在这个例子中,我们创建了一个空的map,可以存储带有字符串键的整数值。我们可以使用方括号符号在map中添加或更新数值,正如下面的代码中提到的:

m["foo"] = 1
m["bar"] = 2

我们也可以使用方括号符号从map中检索数值,如下代码中提到的那样:

fmt.Println(m["foo"]) // 1

然而,map对于并发使用并不安全。如果多个goroutine试图同时访问或修改一个map,就会导致竞赛条件和数据损坏。为了避免这个问题,我们需要使用mutex或其他同步机制来确保每次只有一个goroutine可以访问map。


Go中的Sync.Map

sync.Map类型是Go中的一个内置类型,它提供了一个安全和并发的map替代品,它在go1.9版本中被引入。sync.Map类型使用与maps不同的内部实现,这使得它可以安全地并发使用,而不需要任何额外的同步。

创建sync.Map与创建map类似,只是我们不需要使用make函数:

var m sync.Map

在这个例子中,我们创建了一个空的sync.Map对象。

我们可以使用Store方法在map中添加或更新数值:

m.Store("foo", 1)
m.Store("bar", 2)

我们可以使用Load方法从map中检索数值:

if v, ok := m.Load("foo"); ok {
    fmt.Println(v) // 1
}

如果键存在的话,Load方法会返回与之相关的值,还有一个布尔值,表示该键在map中是否存在。

我们还可以使用Delete方法从map中删除值:

m.Delete("foo")

sync.Map类型还提供了一些其他的方法,比如Range,它允许我们遍历map中的所有键值对。Range方法以一个函数为参数,为map中的每个键值对调用该函数:

m.Range(func(key, value interface{}) bool {
    fmt.Printf("%v: %v\n", key, value)
    return true // continue iterating
})

Range函数返回true以继续迭代,或返回false以停止迭代。


sync.Map和maps之间的区别

Go中的sync.Map和maps有几个区别,如下所述:

1.安全性:

sync.Map对于并发使用是安全的,不需要任何额外的同步,而maps是不安全的,需要一个mutex或其他同步机制来避免竞赛条件。

2.初始化: 

map需要使用make函数进行初始化,而sync.Map则不需要初始化。

3.类型: 

Maps是静态类型,而sync.Map是动态类型,可以存储不同类型的值,不需要事先指定类型。

4.性能: 

在非并发使用中,Maps比sync.Map快,因为它们的开销更少。然而,当多个goroutines访问相同的数据时,sync.Map会更快,因为它消除了锁定和解锁突变的需要。

5.复制:

Maps是按值复制的,而sync.Map是按引用复制的。这意味着,如果我们把一个map传给一个函数,该函数会得到一个map的副本,而如果我们传给一个sync.Map,该函数会得到一个对原始map的引用。


什么时候使用sync.Map

sync.Map应该在我们需要在多个goroutine之间共享数据,并且不想使用mutex或其他同步机制时使用。sync.Map对于重读工作负载很有用,即多个goroutine频繁读取相同的数据,但只有少数goroutine在更新数据。

另一方面,如果我们有一个重写的工作负载,即多个goroutine频繁地更新数据,我们可能需要使用一个mutex或其他同步机制来确保每次只有一个goroutine可以访问数据。

在这种情况下,使用带有互斥器的map可能是一个更好的选择。


让我们看一下如何使用sync.Map的一些例子


例子1: 缓存

sync.Map的一个常见用例是缓存。在这个例子中,我们将创建一个简单的HTTP服务器,以ISO 8601格式返回当前时间。

我们将使用sync.Map来缓存每个请求的响应,这样我们就不需要为每个请求生成一个新的响应。

package main
import (
    "fmt"
    "log"
    "net/http"
    "sync"
    "time"
)
var cache sync.Map
func main() {
    http.HandleFunc("/", handler)
    log.Println(http.ListenAndServe(":8000", nil))
}
func handler(w http.ResponseWriter, r *http.Request) {
    v, ok := cache.Load(r.URL.Path)
    if ok {
        fmt.Fprintln(w, v)
        return
    }
    response := time.Now().UTC().Format(time.RFC3339)
    cache.Store(r.URL.Path, response)
    fmt.Fprintln(w, response)
}

在这个例子中,我们创建了一个名为cache的sync.Map来存储每个请求的响应。

在处理函数中,我们首先使用Load方法检查当前请求的响应是否已经在缓存中。

如果是,我们就把响应写到客户端并提前返回。

否则,我们使用time.Now().UTC().Format(time.RFC3339)生成一个新的响应,使用Store方法将其存储在缓存中,然后写给客户端。


示例2:备忘录化

sync.Map的另一个常见用例是记忆化,这是一种缓存函数结果的技术,以避免对相同的输入进行重新计算。

在这个例子中,我们将创建一个简单的函数来计算第n个斐波那契数,并使用sync.Map来记忆结果以提高性能。

package main
import (
    "fmt"
    "sync"
)
var memo sync.Map
func main() {
    fmt.Println(fib(10))
    fmt.Println(fib(20))
}
func fib(n int) int {
    v, ok := memo.Load(n)
    if ok {
        return v.(int)
    }
    if n < 2 {
        memo.Store(n, n)
        return n
    }
    result := fib(n-1) + fib(n-2)
    memo.Store(n, result)
    return result
}

在这个例子中,我们创建了一个名为memo的sync.Map来存储以前的计算结果。

在fib函数中,我们首先使用Load方法检查当前输入的结果是否已经在memo中。

如果是,我们就提前返回结果。

否则,我们使用公式 fib(n-1) + fib(n-2) 递归计算结果,使用Store方法将其存储在memo中,并返回结果。


总结

总之,map和sync.Map都是Go中有用的数据结构,但它们有不同的使用情况和取舍。

map在非并发使用时速度快且简单,但在并发使用时需要像mutexes这样的同步机制。

sync.Map提供了内置的同步功能,对重读工作负载很有用,但它有一些限制,在非并发使用时比map慢。

通过了解这两种数据结构之间的差异和权衡,我们可以为我们的具体使用情况选择正确的数据结构,并优化我们的代码以提高性能和可扩展性。


  • 上一条:
    为什么通过VPN访问内网数据比直接把服务器暴露在公网更安全?
    下一条:
    在mysql中通过binlog + sql定位是哪台服务器的常驻进程执行了SQL流程步骤
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • 近期文章
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2017-09
    • 2020-03
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-12
    • 2021-01
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-03
    • 2022-04
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-08
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    • 2024-05
    • 2024-06
    • 2024-07
    • 2024-08
    • 2024-11
    • 2025-02
    • 2025-04
    • 2025-05
    • 2025-06
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客