go语言api源码中文版_Go语言学习——sync.map源码剖析

1.简介
最近看了下Sync包,详读了sync.map源码,感觉源码实现还是比较巧妙的,有不少可以学习的地方;在讲源码前,先看下sync.map的"历史",从网上搜资料,sync.map是Go语言在1.9版本才引入的并发安全的map,对此,有些同学心中可能会有个疑问,如果是支持并发,为什么不采取锁map的方式,为啥还要在单独搞个sync.map结构呢?我们先看下锁map存在的问题:

参考:go语言中文文档:www.topgoer.com

转自:https://studygolang.com/topics/12363#reply0
1)mutex + map
最简单的方案就是在map上加个锁,针对map的所有操作都要提前加锁,其存在问题也很明显,锁竞争会非常频繁;
2)rwmutex + map
优化一点,依据场景,如果是读操作多于写操作,可以把mutex换成rwmutex,相比方案一,有一定优化、至少读读之间不会存在互斥,不过,读写之间还会存在阻塞;
根据锁map的优化迭代方案可知,在读读场景下,rwmutex + map可以并发、不存在阻塞,但是,读写还是存在阻塞,而sync.map要做的事情就是能进一步优化:对于map的各种操作,尽可能不阻塞;为此,sync.map采用了两级缓存实现,一级缓存做无锁并发,二级缓存做有锁并发,如:

97184fa981355a63d2c1c6722eb86b42.png


对上图说明两点:
1)针对sync.map的各种操作,都先经过一级缓存,一级缓存采用无锁的方式,只要不出现击穿,即key都在一级缓存中可以找到,则就不会访问到二级缓存;
2)一级缓存和二级缓存之间存在数据同步,二级缓存数据相对更全一些,所以当一级缓存数据比较久时,可以将二级缓存数据同步一下,该情况是在读击穿时处理;在不击穿的前提下,一级缓存中可能有数据删除,数据移除情况也要同步给二级缓存,清除废弃数据、减少空间占用,该情况是在写击穿并且是一、二级缓存都不存在键的情况处理,总之,同步的原则是:一级缓存数据尽可能新;一级缓存数据只能是二级缓存的子集;

2.实现
sync.map的优势是理想情况下以无锁代替有锁、提高性能,但存在击穿后不得不加锁的问题,一旦击穿进入二级缓存,就要进行锁操作了,所以sync.map不太适用于写多读少以及频繁创建新键的情况;因为要考虑击穿问题,所以sync.map的实现也是围绕击穿考虑的。 2.1读操作
读操作比较简单,步骤是:
1)查看一级缓存中是否有key,有就返回对应value;
2)如果没有则进入读击穿,加锁后,在复看一级缓存中是否有key(复看是因为存在二级缓存向一级缓存同步数据的情况),有就返回对应value;
3)如果没有则看二级缓存中有没有,有就返回对应value,此时出现读击穿,会进入读击穿保护机制——击穿达到一定次数,会将二级缓存数据同步到一级缓存;
需要注意的是,在返回value时要检测value的有效性,如果已经废弃(expunged状态),则不用返回。
2.2写操作
写操作相对复杂,根据key是否存在的情况,可以分为create和update,步骤是:
1)查看一级缓存中是否有key,有就尝试更新,之所以是尝试是因为还要检查数据是否已经废弃,如果已经废弃,即使key在一级缓存中存在,也是击穿效果,因为二级缓存中没有;
2)如果一级缓存操作失败,加锁后,在复看一级缓存,如果有key,则更新value,并检测value是否为废弃状态,如果是,则将key、value写入二级缓存;
3)如果一级缓存中一直没有key,但二级缓存中有,则直接更新数据;
4)如果一级缓存和二级缓存都没有key,则将key、value写入二级缓存,此时会尝试将一级缓存数据同步给二级缓存,用于删除废弃数据(将一级缓存中的删除数据设置为expunged状态),因为只有该情况下,一级缓存数据可能是二级缓存数据的子集,所以当插入全新的key时,才会尝试更新缓存数据、移除废弃数据;
2.3删除操作
删除采取的是延迟删除操作,对于待删除数据,其value先设置为nil,优先从一级缓存删除,如果一级缓存没有,再去二级缓存中删除。
2.4源码
以1.14.4版本为例,处理源码是:

func (m *Map) Load(key interface{}) (value interface{}, ok bool) {    read, _ := m.read.Load().(readOnly)    e, ok := read.m[key]        // 一级缓存没有查到key,加锁、复查,amended用于判断一级缓存和二级缓存是否一致    if !ok && read.amended {        m.mu.Lock()        read, _ = m.read.Load().(readOnly)        e, ok = read.m[key]                // 一级缓存还是没有查到key,则击穿进入二级缓存        if !ok && read.amended {            e, ok = m.dirty[key]            m.missLocked()     // 读击穿保护,根据击穿次数决定是否要同步数据        }        m.mu.Unlock()    }    if !ok {        return nil, false    }    return e.load()}func (m *Map) Store(key, value interface{}) {    read, _ := m.read.Load().(readOnly)    if e, ok := read.m[key]; ok && e.tryStore(&value) {        return    }    m.mu.Lock()    read, _ = m.read.Load().(readOnly)    if e, ok := read.m[key]; ok {        // 一级缓存中有key,则更新value,同时,还要查看value是否已经废弃,如果废弃还要将数据写入二级缓存,确保下次同步前,二级缓存数据的完整性,因为操作到二级缓存,所以需要放在锁操作下;这也是为什么tryStore只是尝试存储        if e.unexpungeLocked() {            m.dirty[key] = e        }        e.storeLocked(&value)    } else if e, ok := m.dirty[key]; ok {        e.storeLocked(&value)    } else {        // 对于key完全不存在的情况,尝试数据同步,从一级缓存到二级缓存        if !read.amended {            m.dirtyLocked()        // 数据同步时,废弃数据不会同步,废弃数据会设置为expunged状态            m.read.Store(readOnly{m: read.m, amended: true})        }        m.dirty[key] = newEntry(value)    }    m.mu.Unlock()}// Delete部分相对简单,主要是将value设置为nilfunc (m *Map) Delete(key interface{}) {    read, _ := m.read.Load().(readOnly)    e, ok := read.m[key]    if !ok && read.amended {        m.mu.Lock()        read, _ = m.read.Load().(readOnly)        e, ok = read.m[key]        if !ok && read.amended {            delete(m.dirty, key)        }        m.mu.Unlock()    }    if ok {        e.delete()    }}func (e *entry) delete() (hadValue bool) {    for {        p := atomic.LoadPointer(&e.p)        if p == nil || p == expunged {            return false        }        if atomic.CompareAndSwapPointer(&e.p, p, nil) {            return true        }    }}

3.总结
sync.map是以无锁操作一级缓存的方式支持并发、提高性能,而根据其实现可知,sync.map适用于读多、更新多、新建少的场景(新建情况下,可能会带来较大的开销,比如:读击穿、数据刚从二级缓存同步到一级缓存后,又要新建key,数据又要反向同步一次)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/267508.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JDK自带的log-java.util.logging

配置: 默认情况下,每个 FileHandler 都是使用以下 LogManager 配置属性执行初始化的。如果没有定义属性(或者属性具有非法值),则使用指定的默认值。 java.util.logging.FileHandler.level 为 Handler 指定默认的级别(默…

js实现添加className

obj.className ; //设置为新的obj.className ;//在原来的后面加这个obj.classList.add(" "); //与第一个等价 <style>#side_btn {width: 15%;left: 0;bottom: 5%;}#side_nav {width: 25%;left: -34%;top: 15%;transition: left 1s;text-align: center;}.nav_…

怎么成为日上会员直邮_18个日上直邮问题汇总,可以参考一下哦

前段时间发的日上直邮的微头条和视频&#xff0c;很多朋友遇到一些不明白的地方&#xff0c;我把问题汇总了一下&#xff0c;统一回答一下哦其实因为疫情&#xff0c;很多免税店都在做活动&#xff0c;活动方式不太一样&#xff0c;有好多种&#xff0c;我跟大家分享的只是其中…

IT网址大全

图像处理 [素材]   在线作图[素材]   思缘设计论坛[素材]   ps联盟[素材]   ps学习网[素材]   ps教程论坛[素材]   ps爱好者[素材]   46ps[素材]   勤学网[素材]   艺术字体转换器[模板]   视达千图[模板]   千图网[模板]   千库网[模板]   包图网[模板]   摄图网…

python中sort返回值_Python函数你真的都学会了吗?来看看这篇Python高阶函数!

二、高阶函数高级函数, 英文叫 Higher-order Function.那么什么是高阶函数呢?在说明什么是高阶函数之前, 我们需要对函数再做进一步的理解!2.1 函数的本质函数的本质是什么&#xff1f;函数和函数名到底是一种什么关系&#xff1f;在python中&#xff0c;一切皆对象&#xff0…

拿什么衡量能力?!

众所周知&#xff0c;计算机技术属于高科技的范畴&#xff0c;但不能说所有和计算机沾边的东西都可以称之为高科技&#xff0c;笔者前几天经历了一次面试&#xff0c;也许对于有的朋友来说&#xff0c;这样的经历司空见惯&#xff0c;但是我还是想一吐为快。 本人不才&#xff…

基于数据库的事务消息解决分布式事务方案

转载请注明出处&#xff1a;http://www.cnblogs.com/lizo/p/8516502.html 概述 当单库已不能支撑当前业务的时候&#xff0c;我们往往都考虑进行分库&#xff08;横向拆分或者纵向拆分&#xff09;。但分库有个无法回避的问题&#xff0c;就是事务问题。网上有很多分布式事务解…

websocket 发送给前端一个对象_前端WebSocket封装

场景1: 只有单个长链接&#xff0c;不要求保活class WebSocketClass {constructor() {this.instance null;this.connect();}static getInstance() {if (!this.instance) {this.instance new WebSocketClass();}return this.instance;}connect() {this.ws new WebSocket(ws:/…

POJ1683 Puzzlestan ——Floyd传递闭包+Dfs

好久没写Dfs了&#xff0c;拿来练手。 WA了一次&#xff0c;没有判断中间的情况…… 解法&#xff1a;先用Floyd传递闭包处理哪些点一定要在一起、哪些点一定不能在一起&#xff0c;六重循环。 然后深搜&#xff0c;res[i][j]表示1,i这个物品在j这一行的匹配物品列编号。 没有最…

中service层的作用_浅析Java中dto、dao、service、controller的四层结构

目前我所在的项目组采用的是SpringBoot框架&#xff0c;前端使用BootStrapjQuery。SpringBoot是BS开发框架之一&#xff0c;不用单独开启tomcat服务器&#xff0c;目前比较流行&#xff0c;一般开发大型项目时会将所有的功能细分为许多小模块&#xff0c;每个模块都有dto、dao、…

SCCM 2007系列教程之六使用组策略实现SCCM客户端

SCCM 2007 安装光盘上提供了名为 ConfigMgr2007Installation.adm 的组策略管理模板&#xff0c;可用于配置客户端计算机的安装属性。1、使用 Windows 组策略对象编辑器等编辑器来将管理模板 ConfigMgr2007Installation.adm 导入新的或现有的组策略对象。&#xff08;此文件可以…

强化学习

机器学习算法完整版见fenghaootong-github 强化学习原理&#xff08;RL&#xff09; RL与有监督学习、无监督学习的比较&#xff1a; 有监督的学习是从一个已经标记的训练集中进行学习&#xff0c;训练集中每一个样本的特征可以视为是对该situation的描述&#xff0c;而其labe…

python的tool模块_barktools-包含各种有用的python模块和脚本的包-Oscar Bark Modules Scripts...

作者:Oscar Bark### 作者邮箱:kurshid.ognianovprotonmail.com### 首页:https://github.com/BarkenBark/python-tools### 文档:None### 下载链接# barktoolsA collection of utilities I find useful. Yes.Modulesbase_utilsA collection of modules which only depend on the …

非常不错的Nodejs工具:http-console

http-console是一个用nodejs写的类似于CURL的第三方库文件. 可以很直观的发送http请求以及查看返回结果. 安装需求: 1, 安装nodejs 2, 安装npm 3, npm install http-console 我做了个简单的例子: 启动: http-console 127.0.0.1:3000 1, HTTP GET: http://127.0.0.1:3000/&g…

bzoj1058: [ZJOI2007]报表统计

哈哈set卡时过了。 set求前驱的方法&#xff1a;*--b.lower_bound(x) &#xff08;想想写了splay的肉老师就很愉悦啊&#xff09; 弄两个set&#xff0c;一个记录的是位置的值&#xff0c;一个是差值&#xff0c;MIN_SORT_GAP就很简单啊&#xff0c;插入的时候找前驱后继&#…

javascript对象包含哪些要素_重学JavaScript 对象

栏目为大家介绍JavaScript的对象&#xff0c;重新认识。这里我们继续学习两个比较重要的类型&#xff0c;就是 Object 和 Symbol。我们主要讲的是 Object&#xff0c;相对 Object 来说 Symbol 只是一个配角。关于对象这个概念大家非常早就会接触到了&#xff0c;其实人大概在 5…

【iOS开发必收藏】详解iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!...

Himi 原创&#xff0c; 欢迎转载&#xff0c;转载请在明显处注明&#xff01; 谢谢。 原文地址&#xff1a;http://blog.csdn.net/xiaominghimi/article/details/6937097 终于在11月公司的游戏即将上线了&#xff0c;那么对于iOS游戏来说当今都是内置道具收费属于主流&#xf…

「BZOJ2879」[Noi2012]美食节

这道题就是 「BZOJ1070」[SCOI2007]修车 的加强版 如果一开始把全部边连上会T 优化的方法是只连用到过和下一次增广可能用到的边。 1 #include<bits/stdc.h>2 using namespace std;3 const int N50,M110,NN100010,oo1e9;4 int n,m,cost[N][M],tot,s,t,p[N],rank[NN],c[NN…

不越狱换壁纸_终于来了!iOS 14.3 正式版,可自动定时换壁纸

嘿嘿&#xff0c;我没有猜错吧&#xff01;iOS 14.3 正式版会在12月15日凌晨时段发布&#xff0c;在前几天我就有提到&#xff0c;这一天会发布&#xff0c;主要是与新品 AirPods Max 发售时间与iOS 14.3正式版发布时间一致。其次这次发布iOS 14.3正式版更新内容与 iOS 14.3 RC…

Hibernate【inverse和cascade属性】知识要点

Inverse属性 Inverse属性&#xff1a;表示控制权是否转移.. true:控制权已转移【当前一方没有控制权】false&#xff1a;控制权没有转移【当前一方有控制权】Inverse属性&#xff0c;是在维护关联关系的时候起作用的。只能在“一”的一方中使用该属性&#xff01;Inverse属性的…