欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

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

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

下面是一個簡單的 map 示例:

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

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

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

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

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

首先,讓我們來看一下 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 的實現(xiàn)主要是依賴于一個互斥鎖(sync.Mutex)和兩個 map(read 和 dirty)。其中,read 和 dirty 的作用分別是什么呢?我們先來看一下 read 的定義:

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

可以看到,read 只有一個成員 m,它是一個 map 類型。而 amended 則表示 read 中的鍵值對是否被修改過。接下來,我們來看一下 dirty 的定義:

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

和 read 不同的是,dirty 中包含了兩個 map:m 和 dirty。其中,m 存儲了被修改過的鍵值對,而 dirty 則存儲了哪些鍵值對被修改過。

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

在 sync.Map 中,讀取操作非常簡單,直接從 readOnly 中的 m 中查找即可。如果 readOnly 中的鍵值對被修改過,則需要從 dirty 中查找。讀取操作的實現(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 中查找鍵值對。如果鍵值對不存在且 readOnly 中的鍵值對被修改過,則需要獲取互斥鎖,并重新從 readOnly 中查找。如果還是沒有找到,那么就從 dirty 中查找。

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

在 sync.Map 中,寫入操作需要分兩步完成。首先,我們需要判斷 readOnly 中的鍵值對是否被修改過,如果沒有被修改過,則直接將鍵值對添加到 readOnly 中的 m 中即可。否則,我們需要獲取互斥鎖,然后將鍵值對添加到 dirty 中的 m 中,并將對應(yīng)的鍵添加到 dirty 中的 dirty 中。寫入操作的實現(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 中查找鍵值對。如果鍵值對不存在且 readOnly 中的鍵值對沒有被修改過,則需要獲取互斥鎖,并重新從 readOnly 中查找。如果還是沒有找到,則將鍵值對添加到 readOnly 中的 m 中,并將 amended 設(shè)置為 true。否則,我們需要獲取互斥鎖,并將鍵值對添加到 dirty 中的 m 中,并將對應(yīng)的鍵添加到 dirty 中的 dirty 中。如果 dirty 中已經(jīng)存在該鍵,則只需要更新 dirty 中的鍵值即可。如果 dirty 中沒有該鍵,則需要在 dirty 中添加該鍵,并將該鍵的 dirty 置為 true。

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

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

在 sync.Map 中,刪除操作也需要分兩步完成。首先,我們需要判斷 readOnly 中的鍵值對是否被修改過,如果沒有被修改過,則直接從 readOnly 中的 m 中刪除鍵值對即可。否則,我們需要獲取互斥鎖,然后將鍵添加到 dirty 中的 dirty 中,并將 dirty 中的對應(yīng)鍵的值設(shè)置為 false。刪除操作的實現(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 中查找鍵值對。如果鍵值對存在或者 readOnly 中的鍵值對被修改過,則需要獲取互斥鎖,并重新從 readOnly 中查找。如果還是沒有找到,則將鍵添加到 dirty 中的 dirty 中,并將 dirty 中的對應(yīng)鍵的值設(shè)置為 false。接下來,我們需要判斷 dirty 是否為 nil,如果為 nil,則需要將 dirty 初始化為一個空 map。然后,我們將鍵添加到 dirty 中,并將 dirty 中的對應(yīng)鍵的值設(shè)置為 true。最后,我們將 readOnly 中的 amended 設(shè)置為 true,并將 readOnly 存儲回 read 中。

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

在 sync.Map 中,遍歷操作需要將 readOnly 和 dirty 中的所有鍵值對進行合并,并返回所有未被刪除的鍵值對。遍歷操作的實現(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 中獲取所有的鍵值對,并檢查是否有鍵值對被修改過。如果鍵值對被修改過,則需要獲取互斥鎖,并將 readOnly 和 dirty 中的鍵值對合并,然后將合并后的鍵值對存儲回 readOnly 中,并將 dirty 設(shè)置為 nil。接下來,我們遍歷 readOnly 中的所有鍵值對,并調(diào)用 f 函數(shù)來處理鍵值對。如果 f 函數(shù)返回 false,則遍歷過程結(jié)束。

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

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

相關(guān)文章

  • go語言中的udp協(xié)議及TCP通訊實現(xiàn)示例

    go語言中的udp協(xié)議及TCP通訊實現(xiàn)示例

    這篇文章主要為大家介紹了go語言中的udp協(xié)議及TCP通訊的實現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪
    2022-04-04
  • Golang依賴注入工具digo的使用詳解

    Golang依賴注入工具digo的使用詳解

    這篇文章主要為大家詳細介紹了Golang中依賴注入工具digo的使用,文中的示例代碼講解詳細,具有一定的學(xué)習(xí)價值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-06-06
  • golang踩坑實戰(zhàn)之channel的正確使用方式

    golang踩坑實戰(zhàn)之channel的正確使用方式

    Golang?channel是Go語言中一個非常重要的特性,除了用來處理并發(fā)編程的任務(wù)中,它還可以用來進行消息傳遞和事件通知,這篇文章主要給大家介紹了關(guān)于golang踩坑實戰(zhàn)之channel的正確使用方式,需要的朋友可以參考下
    2023-06-06
  • GO的鎖和原子操作的示例詳解

    GO的鎖和原子操作的示例詳解

    這篇文章主要為大家詳細介紹了Go語言中鎖和原子操作的相關(guān)資料,文中的示例代碼講解詳細,對我們學(xué)習(xí)Go語言有一定的幫助,需要的可以參考一下
    2023-02-02
  • Golang限流庫與漏桶和令牌桶的使用介紹

    Golang限流庫與漏桶和令牌桶的使用介紹

    這篇文章主要介紹了golang限流庫以及漏桶與令牌桶的實現(xiàn)原理,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-03-03
  • go中import包的大坑解決方案

    go中import包的大坑解決方案

    最近開始使用Go/GoLand 在import 自定義包時出現(xiàn)各種狀況,本文就介紹一下go中import包的大坑解決方案,具有一定的參考價值,感興趣 可以了解一下
    2022-06-06
  • 使用Golang的Context管理上下文的方法

    使用Golang的Context管理上下文的方法

    這篇文章主要介紹了使用Golang的Context管理上下文的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • 深入分析Golang Server源碼實現(xiàn)過程

    深入分析Golang Server源碼實現(xiàn)過程

    這篇文章深入介紹了Golang Server源碼實現(xiàn)過程,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-02-02
  • Golang自動追蹤GitHub上熱門AI項目

    Golang自動追蹤GitHub上熱門AI項目

    這篇文章主要為大家介紹了Golang自動追蹤GitHub上熱門AI項目,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • Go語言中l(wèi)og日志庫的介紹

    Go語言中l(wèi)og日志庫的介紹

    本文給大家介紹Go語言中l(wèi)og日志庫的概念使用技巧,log包定義了Logger類型,該類型提供了一些格式化輸出的方法,本文通過示例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-10-10

最新評論