亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一文帶你深入探究Go語(yǔ)言中的sync.Map

 更新時(shí)間:2023年04月27日 14:59:39   作者:金刀大菜牙  
在?Go?語(yǔ)言中,有一個(gè)非常實(shí)用的并發(fā)安全的?Map?實(shí)現(xiàn):sync.Map,它是在?Go?1.9?版本中引入的。本文我們將深入探討?sync.Map?的基本原理,幫助讀者更好地理解并使用這個(gè)并發(fā)安全的?Map

在 Go 語(yǔ)言中,有一個(gè)非常實(shí)用的并發(fā)安全的 Map 實(shí)現(xiàn):sync.Map,它是在 Go 1.9 版本中引入的。相比于標(biāo)準(zhǔn)庫(kù)中的 map,它的最大特點(diǎn)就是可以在并發(fā)情況下安全地讀寫,而不需要加鎖。在這篇博客中,我們將深入探討 sync.Map 的基本原理,幫助讀者更好地理解并使用這個(gè)并發(fā)安全的 Map。

1. Map 的基本實(shí)現(xiàn)原理

在介紹 sync.Map 的基本實(shí)現(xiàn)原理之前,我們需要先了解一下 Go 語(yǔ)言標(biāo)準(zhǔn)庫(kù)中的 map 實(shí)現(xiàn)原理。在 Go 中,map 是基于哈希表實(shí)現(xiàn)的。當(dāng)我們向 map 中添加元素時(shí),它會(huì)根據(jù) key 計(jì)算出一個(gè)哈希值,然后將這個(gè)值映射到一個(gè)桶中。如果該桶中已經(jīng)有了元素,它會(huì)遍歷桶中的元素,查找是否已經(jīng)存在相同的 key,如果存在就更新對(duì)應(yīng)的值,否則就添加一個(gè)新的鍵值對(duì)。

下面是一個(gè)簡(jiǎn)單的 map 示例:

m := make(map[string]int)
m["a"] = 1
m["b"] = 2
fmt.Println(m["a"]) // Output: 1

當(dāng)我們運(yùn)行這段代碼時(shí),Go 語(yǔ)言會(huì)自動(dòng)幫我們分配一個(gè)哈希表和若干個(gè)桶,然后將鍵值對(duì)添加到對(duì)應(yīng)的桶中。這樣,當(dāng)我們需要訪問(wèn)某個(gè) key 對(duì)應(yīng)的值時(shí),Go 語(yǔ)言會(huì)根據(jù)哈希值快速定位到對(duì)應(yīng)的桶,然后遍歷桶中的元素,查找是否有相同的 key,如果找到了就返回對(duì)應(yīng)的值。

2. sync.Map 的實(shí)現(xiàn)原理

sync.Map 是 Go 語(yǔ)言標(biāo)準(zhǔn)庫(kù)中的一個(gè)并發(fā)安全的 Map 實(shí)現(xiàn),它可以在并發(fā)情況下安全地讀寫,而不需要加鎖。那么,它是如何實(shí)現(xiàn)這種并發(fā)安全性的呢?下面我們就來(lái)一步步地解析 sync.Map 的實(shí)現(xiàn)原理。

2.1 sync.Map 的結(jié)構(gòu)體定義

首先,讓我們來(lái)看一下 sync.Map 的結(jié)構(gòu)體定義:

type Map struct {
    mu      sync.Mutex
    read    atomic.Value // readOnly
    dirty   map[interface{}]interface{}
    misses  int
    dirtyLocked uintptr
}

從上面的代碼中可以看出,sync.Map 的實(shí)現(xiàn)主要是依賴于一個(gè)互斥鎖(sync.Mutex)和兩個(gè) map(read 和 dirty)。其中,read 和 dirty 的作用分別是什么呢?我們先來(lái)看一下 read 的定義:

type readOnly struct {
    m       map[interface{}]interface{}
    amended bool
}

可以看到,read 只有一個(gè)成員 m,它是一個(gè) map 類型。而 amended 則表示 read 中的鍵值對(duì)是否被修改過(guò)。接下來(lái),我們來(lái)看一下 dirty 的定義:

type dirty struct {
    m       map[interface{}]interface{}
    dirty   map[interface{}]bool
    misses  int
}

和 read 不同的是,dirty 中包含了兩個(gè) map:m 和 dirty。其中,m 存儲(chǔ)了被修改過(guò)的鍵值對(duì),而 dirty 則存儲(chǔ)了哪些鍵值對(duì)被修改過(guò)。

2.2 sync.Map 的讀取實(shí)現(xiàn)

在 sync.Map 中,讀取操作非常簡(jiǎn)單,直接從 readOnly 中的 m 中查找即可。如果 readOnly 中的鍵值對(duì)被修改過(guò),則需要從 dirty 中查找。讀取操作的實(shí)現(xiàn)代碼如下:

func (m *Map) Load(key interface{}) (value interface{}, ok bool) {
    read, _ := m.read.Load().(readOnly)
    value, ok = read.m[key]
    if !ok && read.amended {
        m.mu.Lock()
        read, _ = m.read.Load().(readOnly)
        value, ok = read.m[key]
        if !ok && read.amended {
            value, ok = read.m[key]
        }
        m.mu.Unlock()
    }
    return
}

在這段代碼中,我們首先從 readOnly 中的 m 中查找鍵值對(duì)。如果鍵值對(duì)不存在且 readOnly 中的鍵值對(duì)被修改過(guò),則需要獲取互斥鎖,并重新從 readOnly 中查找。如果還是沒(méi)有找到,那么就從 dirty 中查找。

2.3 sync.Map 的寫入實(shí)現(xiàn)

在 sync.Map 中,寫入操作需要分兩步完成。首先,我們需要判斷 readOnly 中的鍵值對(duì)是否被修改過(guò),如果沒(méi)有被修改過(guò),則直接將鍵值對(duì)添加到 readOnly 中的 m 中即可。否則,我們需要獲取互斥鎖,然后將鍵值對(duì)添加到 dirty 中的 m 中,并將對(duì)應(yīng)的鍵添加到 dirty 中的 dirty 中。寫入操作的實(shí)現(xiàn)代碼如下:

func (m *Map) Store(key, value interface{}) {
    read, _ := m.read.Load().(readOnly)
    if v, ok := read.m[key]; !ok && !read.amended {
        m.mu.Lock()
        read, _ = m.read.Load().(readOnly)
        if v, ok := read.m[key]; !ok {
            read = readOnly{m: read.m, amended: true}
        }
        read.m[key] = value
        m.read.Store(read)
        m.mu.Unlock()
    } else {
        m.mu.Lock()
        dirty := m.dirtyLocked != 0
        if !dirty {
            m.dirtyLocked = 1
            m.dirty = make(map[interface{}]interface{})
        }
        m.dirty[key] = value
        if !ok {
            m.dirty[key] = value
            m.dirty[key] = true
        }
        if dirty {
            m.mu.Unlock()
            return
        }
        m.read.Store(readOnly{m: read.m, amended: true})
        m.mu.Unlock()
    }
}

在這段代碼中,我們首先從 readOnly 中的 m 中查找鍵值對(duì)。如果鍵值對(duì)不存在且 readOnly 中的鍵值對(duì)沒(méi)有被修改過(guò),則需要獲取互斥鎖,并重新從 readOnly 中查找。如果還是沒(méi)有找到,則將鍵值對(duì)添加到 readOnly 中的 m 中,并將 amended 設(shè)置為 true。否則,我們需要獲取互斥鎖,并將鍵值對(duì)添加到 dirty 中的 m 中,并將對(duì)應(yīng)的鍵添加到 dirty 中的 dirty 中。如果 dirty 中已經(jīng)存在該鍵,則只需要更新 dirty 中的鍵值即可。如果 dirty 中沒(méi)有該鍵,則需要在 dirty 中添加該鍵,并將該鍵的 dirty 置為 true。

接下來(lái),我們需要判斷 dirty 是否被鎖定。如果 dirty 被鎖定,則直接退出函數(shù)。否則,我們需要將 readOnly 中的 amended 設(shè)置為 true,并將 readOnly 存儲(chǔ)回 read 中。

2.4 sync.Map 的刪除實(shí)現(xiàn)

在 sync.Map 中,刪除操作也需要分兩步完成。首先,我們需要判斷 readOnly 中的鍵值對(duì)是否被修改過(guò),如果沒(méi)有被修改過(guò),則直接從 readOnly 中的 m 中刪除鍵值對(duì)即可。否則,我們需要獲取互斥鎖,然后將鍵添加到 dirty 中的 dirty 中,并將 dirty 中的對(duì)應(yīng)鍵的值設(shè)置為 false。刪除操作的實(shí)現(xiàn)代碼如下:

func (m *Map) Delete(key interface{}) {
    read, _ := m.read.Load().(readOnly)
    if _, ok := read.m[key]; ok || read.amended {
        m.mu.Lock()
        read, _ = m.read.Load().(readOnly)
        if _, ok := read.m[key]; ok || read.amended {
            if m.dirty == nil {
                m.dirty = make(map[interface{}]interface{})
            }
            m.dirty[key] = false
            m.dirty[key] = true
            m.read.Store(readOnly{m: read.m, amended: true})
        }
        m.mu.Unlock()
    }
}

在這段代碼中,我們首先從 readOnly 中的 m 中查找鍵值對(duì)。如果鍵值對(duì)存在或者 readOnly 中的鍵值對(duì)被修改過(guò),則需要獲取互斥鎖,并重新從 readOnly 中查找。如果還是沒(méi)有找到,則將鍵添加到 dirty 中的 dirty 中,并將 dirty 中的對(duì)應(yīng)鍵的值設(shè)置為 false。接下來(lái),我們需要判斷 dirty 是否為 nil,如果為 nil,則需要將 dirty 初始化為一個(gè)空 map。然后,我們將鍵添加到 dirty 中,并將 dirty 中的對(duì)應(yīng)鍵的值設(shè)置為 true。最后,我們將 readOnly 中的 amended 設(shè)置為 true,并將 readOnly 存儲(chǔ)回 read 中。

2.5 sync.Map 的遍歷實(shí)現(xiàn)

在 sync.Map 中,遍歷操作需要將 readOnly 和 dirty 中的所有鍵值對(duì)進(jìn)行合并,并返回所有未被刪除的鍵值對(duì)。遍歷操作的實(shí)現(xiàn)代碼如下:

func (m *Map) Range(f func(key, value interface{}) bool) {
    read, _ := m.read.Load().(readOnly)
    if read.amended {
        m.mu.Lock()
        read, _ = m.read.Load().(readOnly)
        if read.amended {
            read = readOnly{
                m: merge(read.m, m.dirty),
            }
            read.amended = false
            m.read.Store(read)
            m.dirty = nil
        }
        m.mu.Unlock()
    }
    for k, v := range read.m {
        if !f(k, v) {
            break
        }
    }
}
?
func merge(m1, m2 map[interface{}]interface{}) map[interface{}]interface{} {
    if len(m1) == 0 && len(m2) == 0 {
        return nil
    }
    if len(m1) == 0 {
        return m2
    }
    if len(m2) == 0 {
        return m1
    }
    m := make(map[interface{}]interface{})
    for k, v := range m1 {
        m[k] = v
    }
    for k, v := range m2 {
        if _, ok := m[k]; !ok || !v.(bool) {
            m[k] = v
        }
    }
    return m
}

在這段代碼中,我們首先從 readOnly 中獲取所有的鍵值對(duì),并檢查是否有鍵值對(duì)被修改過(guò)。如果鍵值對(duì)被修改過(guò),則需要獲取互斥鎖,并將 readOnly 和 dirty 中的鍵值對(duì)合并,然后將合并后的鍵值對(duì)存儲(chǔ)回 readOnly 中,并將 dirty 設(shè)置為 nil。接下來(lái),我們遍歷 readOnly 中的所有鍵值對(duì),并調(diào)用 f 函數(shù)來(lái)處理鍵值對(duì)。如果 f 函數(shù)返回 false,則遍歷過(guò)程結(jié)束。

在這個(gè) Range 函數(shù)中,我們還實(shí)現(xiàn)了一個(gè)名為 merge 的輔助函數(shù),用于合并兩個(gè) map。在合并過(guò)程中,我們首先判斷兩個(gè) map 是否為空,如果為空,則直接返回 nil。如果其中一個(gè) map 為空,則返回另一個(gè) map。否則,我們需要將 m1 中的鍵值對(duì)全部添加到新的 map 中,并逐個(gè)遍歷 m2 中的鍵值對(duì)。如果 m2 中的鍵不存在于新的 map 中,或者 m2 中的鍵被刪除,則將其添加到新的 map 中。

以上就是一文帶你深入探究Go語(yǔ)言中的sync.Map的詳細(xì)內(nèi)容,更多關(guān)于Go語(yǔ)言sync.Map的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • golang分層測(cè)試之http接口測(cè)試入門教程

    golang分層測(cè)試之http接口測(cè)試入門教程

    這篇文章主要介紹了golang分層測(cè)試之http接口測(cè)試入門教程,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-12-12
  • Golang中的關(guān)鍵字(defer、:=、go?func())詳細(xì)解讀

    Golang中的關(guān)鍵字(defer、:=、go?func())詳細(xì)解讀

    這篇文章主要介紹了Golang中的關(guān)鍵字(defer、:=、go?func())詳細(xì)解讀,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04
  • golang根據(jù)URL獲取文件名的示例代碼

    golang根據(jù)URL獲取文件名的示例代碼

    這篇文章主要為大家詳細(xì)介紹了golang根據(jù)URL獲取文件名,文中的示例代碼講解詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-01-01
  • go語(yǔ)言實(shí)現(xiàn)文件分割的方法

    go語(yǔ)言實(shí)現(xiàn)文件分割的方法

    這篇文章主要介紹了go語(yǔ)言實(shí)現(xiàn)文件分割的方法,實(shí)例分析了Go語(yǔ)言操作文件的技巧,需要的朋友可以參考下
    2015-03-03
  • 一文詳解go的defer和return的執(zhí)行順序

    一文詳解go的defer和return的執(zhí)行順序

    go的defer和return是golang中的兩個(gè)關(guān)鍵字,return用于返回函數(shù)的返回值,也可以參與一定的流程控制,defer是golang中的延遲調(diào)用,經(jīng)常用于文件流的關(guān)閉,鎖的解鎖操作,本文給大家介紹了go的defer和return的執(zhí)行順序,需要的朋友可以參考下
    2024-07-07
  • Go語(yǔ)言的互斥鎖的詳細(xì)使用

    Go語(yǔ)言的互斥鎖的詳細(xì)使用

    本文主要介紹了Go語(yǔ)言的互斥鎖的詳細(xì)使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • go語(yǔ)言中strings包的用法匯總

    go語(yǔ)言中strings包的用法匯總

    Golang語(yǔ)言 strings標(biāo)準(zhǔn)庫(kù)包主要涉及字符串的基本操作,下面我們來(lái)詳細(xì)分析下吧
    2018-10-10
  • Golang學(xué)習(xí)筆記之延遲函數(shù)(defer)的使用小結(jié)

    Golang學(xué)習(xí)筆記之延遲函數(shù)(defer)的使用小結(jié)

    這篇文章主要介紹了Golang學(xué)習(xí)筆記之延遲函數(shù)(defer),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-12-12
  • go格式“占位符”輸入輸出 類似python的input

    go格式“占位符”輸入輸出 類似python的input

    這篇文章主要介紹了go格式“占位符”, 輸入輸出,類似python的input,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-04-04
  • golang 對(duì)私有函數(shù)進(jìn)行單元測(cè)試的實(shí)例

    golang 對(duì)私有函數(shù)進(jìn)行單元測(cè)試的實(shí)例

    這篇文章主要介紹了golang 對(duì)私有函數(shù)進(jìn)行單元測(cè)試的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-05-05

最新評(píng)論