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.pswp.cn/web/38264.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/38264.shtml
英文地址,請注明出處:http://en.pswp.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;幫助投資者在交易中盡量…