go sync包(七)Sync.Map

Sync.Map

原理

  • 通过 read 和 dirty 两个字段实现数据的读写分离,读的数据存在只读字段 read 上,将最新写入的数据存在 dirty 字段上。
  • 读取时会先查询 read,不存在再查询 dirty,写入时则只写入 dirty。
  • 读取 read 并不需要加锁,而读或写 dirty 则需要加锁。
  • misses 字段来统计 read 被穿透的次数(被穿透指需要读 dirty 的情况),超过一定次数则将 dirty 数据更新到 read 中(触发条件:misses=len(dirty))。

优缺点

  • 优点:通过读写分离,降低锁时间来提高效率。
  • 缺点:不适用于大量写的场景,这样会导致 read map 读不到数据而进一步加锁读取,同时dirty map也会一直晋升为read map,整体性能较差,甚至没有单纯的 map+metux 高。
  • 适用场景:读多写少的场景。

Sync.Map 数据结构

type Map struct {mu Mutexread atomic.Value // readOnly// 包含需要加锁才能访问的元素// 包括所有在read字段中但未被expunged(删除)的元素以及新加的元素dirty map[any]*entry// 记录从read中读取miss的次数,一旦miss数和dirty长度一样了,就会把dirty提升为read    misses int
}// readOnly is an immutable struct stored atomically in the Map.read field.
type readOnly struct {m       map[any]*entryamended bool // true if the dirty map contains some key not in m.
}// expunged is an arbitrary pointer that marks entries which have been deleted
// from the dirty map.
var expunged = unsafe.Pointer(new(any))// An entry is a slot in the map corresponding to a particular key.
type entry struct {p unsafe.Pointer // *interface{}
}

Sync.Map 操作

Store
// Store sets the value for a key.
func (m *Map) Store(key, value any) {// 判断 read 有没有包含这个 key,有则 cas 更新read, _ := m.read.Load().(readOnly)if e, ok := read.m[key]; ok && e.tryStore(&value) {return}// read 没有,需要加锁访问 dirtym.mu.Lock()read, _ = m.read.Load().(readOnly)// 双重检查,在查一下 read 里面有没有(包含标记删除的)if e, ok := read.m[key]; ok {// 之前被标记为删除,重新加入 dirtyif e.unexpungeLocked() {// The entry was previously expunged, which implies that there is a// non-nil dirty map and this entry is not in it.m.dirty[key] = e}e.storeLocked(&value)} else if e, ok := m.dirty[key]; ok {e.storeLocked(&value)} else {if !read.amended {// We're adding the first new key to the dirty map.// Make sure it is allocated and mark the read-only map as incomplete.// 创建一个新的 dirty,遍历 read,将没有标记删除的加入m.dirtyLocked()m.read.Store(readOnly{m: read.m, amended: true})}m.dirty[key] = newEntry(value)}m.mu.Unlock()
}func (m *Map) dirtyLocked() {if m.dirty != nil {return}read, _ := m.read.Load().(readOnly)m.dirty = make(map[any]*entry, len(read.m))for k, e := range read.m {if !e.tryExpungeLocked() {m.dirty[k] = e}}
}

Store 是 新增/修改 操作:

  1. read 里面有,直接 cas 更新。
  2. read 没有,加锁。
  3. 再次检查 read,有已经标记删除的,重用,并更新 value。
  4. dirty 有,直接更新。
  5. dirty 没有,新增一个。如果 dirty 为 nil,创建一个 dirty,遍历 read 将元素加入 dirty。
Load
// Load returns the value stored in the map for a key, or nil if no
// value is present.
// The ok result indicates whether value was found in the map.
func (m *Map) Load(key any) (value any, ok bool) {// 先从 read 获取read, _ := m.read.Load().(readOnly)e, ok := read.m[key]// dirty 中有包含 read 中没有的元素if !ok && read.amended {// 加锁,双检查 read,read 中没有,从 dirty 中获取m.mu.Lock()// Avoid reporting a spurious miss if m.dirty got promoted while we were// blocked on m.mu. (If further loads of the same key will not miss, it's// not worth copying the dirty map for this key.)read, _ = m.read.Load().(readOnly)e, ok = read.m[key]if !ok && read.amended {e, ok = m.dirty[key]// Regardless of whether the entry was present, record a miss: this key// will take the slow path until the dirty map is promoted to the read// map.// miss++m.missLocked()}m.mu.Unlock()}if !ok {return nil, false}return e.load()
}func (m *Map) missLocked() {m.misses++if m.misses < len(m.dirty) {return}// dirty 提升为 readm.read.Store(readOnly{m: m.dirty})// dirty 置为 nilm.dirty = nilm.misses = 0
}

Load 是 读取 操作:

  1. 从 read 中获取,有则直接返回。
  2. read 中没有,加锁
    • 双检查 read, read 有,赋值。
    • read 没有,从 dirty 中获取,不管 dirty 中有没有,miss++
      miss == len(dirty),dirty 提升为 read 字段,清空 dirty。
Delete
// Delete deletes the value for a key.
func (m *Map) Delete(key any) {m.LoadAndDelete(key)
}// LoadAndDelete deletes the value for a key, returning the previous value if any.
// The loaded result reports whether the key was present.
func (m *Map) LoadAndDelete(key any) (value any, loaded bool) {// 从 read 中获取read, _ := m.read.Load().(readOnly)e, ok := read.m[key]if !ok && read.amended {// 加锁,双重检查 readm.mu.Lock()read, _ = m.read.Load().(readOnly)e, ok = read.m[key]if !ok && read.amended {// read 没有,dirty 有,则直接删除 dirty 的keye, ok = m.dirty[key]delete(m.dirty, key)// Regardless of whether the entry was present, record a miss: this key// will take the slow path until the dirty map is promoted to the read// map.m.missLocked()}m.mu.Unlock()}if ok {// 延迟删除:read 的 entry 标记为删除,设置为 nilreturn e.delete()}return nil, false
}func (e *entry) delete() (value any, ok bool) {for {p := atomic.LoadPointer(&e.p)if p == nil || p == expunged {return nil, false}if atomic.CompareAndSwapPointer(&e.p, p, nil) {return *(*any)(p), true}}
}

Delete 是 删除 操作:

  1. 从 read 中获取,有则赋值。
  2. read 没有,加锁
    • 双重检查 read,有则赋值。
    • read 没有,dirty 有,删除 dirty 中的值,给 entry 赋值,miss++。
  3. 延迟删除:read 的 entry 标记为删除,设置为 nil。

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

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

相关文章

每天一个数据分析题(三百九十九)- 逻辑回归

逻辑回归中&#xff0c;若选0.5作为阈值区分正负样本&#xff0c;其决策平面是&#xff08; &#xff09; A. wxb&#xff1d; 0 B. wxb&#xff1d; 1 C. wxb&#xff1d; -1 D. wxb&#xff1d; 2 数据分析认证考试介绍&#xff1a;点击进入 题目来源于CDA模拟题库 点…

Python实现万花筒效果:创造炫目的动态图案

文章目录 引言准备工作前置条件 代码实现与解析导入必要的库初始化Pygame定义绘制万花筒图案的函数主循环 完整代码 引言 万花筒效果通过反射和旋转图案创造出美丽的对称图案。在这篇博客中&#xff0c;我们将使用Python来实现一个动态的万花筒效果。通过利用Pygame库&#xf…

大数据可视化实验(八):大数据可视化综合实训

目录 一、实验目的... 1 二、实验环境... 1 三、实验内容... 1 1&#xff09;Python纵向柱状图实训... 1 2&#xff09;Python水平柱状图实训... 3 3&#xff09;Python多数据并列柱状图实训.. 3 4&#xff09;Python折线图实训... 4 5&#xff09;Python直方图实训...…

PAT 1108 Finding Average

原题链接&#xff1a;PAT 1108 Finding Average The basic task is simple: given N real numbers, you are supposed to calculate their average. But what makes it complicated is that some of the input numbers might not be legal. A legal input is a real number in…

Python只读取Excel文件的一部分数据,比如特定范围的行和列?

如何只读取Excel文件的一部分数据&#xff0c;比如特定范围的行和列&#xff1f; 在Python中&#xff0c;如果你只想读取Excel文件的特定范围&#xff0c;可以使用以下方法&#xff1a; pandas: Pandas是一个强大的数据处理库&#xff0c;它有一个内置函数read_excel()用于读…

在不修改.gitignore的情况下,忽略个人文件的提交

Git提供了一个assume-unchanged命令&#xff0c;可以将文件标记为“假设未更改”。这意味着Git将忽略该文件的更改&#xff0c;不会将其提交到仓库中。要使用该命令&#xff0c;只需运行以下命令&#xff1a; git update-index --assume-unchanged <file>其中&#xff0…

边缘网关带来的效益探讨-天拓四方

边缘网关作为连接物理世界与数字世界的桥梁&#xff0c;在智能制造、智慧城市等各个领域中发挥着关键作用。通过收集、处理来自各种传感器和设备的数据&#xff0c;边缘网关为实时决策、优化生产流程以及提高运营效率提供了强有力的支持。下面我们将探讨边缘网关带来的效益。 …

链路全贯通,价值引领数据能力升级|爱分析报告

数据能力已经成为企业的核心竞争力。政策驱动数据产业发展加速&#xff0c;如2023年国家数据局成立&#xff1b;2024年&#xff0c;《“数据要素”三年行动计划&#xff08;2024-2026年&#xff09;》正式发布&#xff1b;并且 2024年起正式将数据资源视为资产纳入财务报表&…

C++——list类用法指南

一、list的介绍 1、list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代 2、list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前一个元素和后一个元素 …

yum安装并初始化PG

获取yum源 从官网获取yum源&#xff0c;并安装rpm包 sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm安装 yum install -y postgresql14-server启动并初始化 sudo /usr/pgsql-14/bin/postgre…

机器学习-EM算法

目录 一:最大似然估计 二:EM算法 加入隐变量 EM算法推导 1.Jensen不等式 点个赞呗!!! 一:最大似然估计 举个栗子:一个袋子里有很多个球,每次放回的取一个球,取了十次,其中有六次白球,4次黑球。那么就认为袋子里面取到白球的概率为6/10,黑球的概率为4/10。…

pytorch-01

加载mnist数据集 one-hot编码实现 import numpy as np import torch x_train np.load("../dataset/mnist/x_train.npy") # 从网站提前下载数据集&#xff0c;并解压缩 y_train_label np.load("../dataset/mnist/y_train_label.npy") x torch.tensor(y…

Vue 全局状态管理新宠:Pinia实战指南

文章目录 前言全局状态管理基本步骤&#xff1a;pinia 前言 随着Vue.js项目的日益复杂&#xff0c;高效的状态管理变得至关重要。Pinia作为Vue.js官方推荐的新一代状态管理库&#xff0c;以其简洁的API和强大的功能脱颖而出。本文将带您快速上手Pinia&#xff0c;从安装到应用&…

uniapp如何根据不同角色自定义不同的tabbar

思路&#xff1a; 1.第一种是根据登录时获取的不同角色信息&#xff0c;来进行 跳转到不同的页面&#xff0c;在这些页面中使用自定义tabbar 2.第二种思路是封装一个自定义tabbar组件&#xff0c;然后在所有要展示tabbar的页面中引入使用 1.根据手机号码一键登录&#xff0c…

SpringMVC的基本使用

SpringMVC简介 SpringMVC是Spring提供的一套建立在Servlet基础上&#xff0c;基于MVC模式的web解决方案 SpringMVC核心组件 DispatcherServlet&#xff1a;前置控制器&#xff0c;来自客户端的所有请求都经由DispatcherServlet进行处理和分发Handler&#xff1a;处理器&…

三个方法教大家学会RAR文件转换为ZIP格式

在日常工作当中&#xff0c;RAR和ZIP是两种常见的压缩文件格式。有时候&#xff0c;大家可能会遇到将RAR文件转换为ZIP格式的情况&#xff0c;这通常是为了方便在特定情况下打开或使用文件。下面给大家分享几个RAR文件转换为ZIP格式的方法&#xff0c;下面随小编一起来看看吧~ …

在mfc程序中,如何用c++找到exe文件所在的路径

在 MFC&#xff08;Microsoft Foundation Class&#xff09;程序中&#xff0c;你可以使用 GetModuleFileName 函数来获取当前运行的可执行文件&#xff08;.exe&#xff09;的路径。 以下是一个示例代码&#xff1a; #include <afxwin.h> #include <iostream>in…

KVM性能优化之CPU优化

1、查看kvm虚拟机vCPU的QEMU线程 ps -eLo ruser,pid,ppid,lwp,psr,args |awk /^qemu/{print $1,$2,$3,$4,$5,$6,$8} 注:vcpu是不同的线程&#xff0c;而不同的线程是跑在不同的cpu上&#xff0c;一般情况&#xff0c;虚拟机在运行时自身会点用3个cpus&#xff0c;为保证生产环…

通过MATLAB控制TI毫米波雷达的工作状态

前言 前一章博主介绍了MATLAB上位机软件“设计视图”的制作流程,这一章节博主将介绍如何基于这些组件结合MATLAB代码来发送CFG指令控制毫米波雷达的工作状态 串口配置 首先,在我们选择的端口号输入框和端口波特率设置框内是可以手动填入数值(字符)的,也可以在点击运行后…

汇凯金业:投资交易如何才能不亏损

投资交易中永不亏损是一个理想化的目标&#xff0c;现实中无法完全避免亏损。然而&#xff0c;通过科学的方法、合理的策略和严格的风险管理&#xff0c;投资者可以大幅减少亏损&#xff0c;并提高长期盈利的概率。以下是一些关键策略和方法&#xff0c;帮助投资者在交易中尽量…