国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Go 語言 map 是并發(fā)安全的嗎?

這篇具有很好參考價值的文章主要介紹了Go 語言 map 是并發(fā)安全的嗎?。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

原文鏈接: Go 語言 map 是并發(fā)安全的嗎?

Go 語言中的 map 是一個非常常用的數(shù)據(jù)結(jié)構(gòu),它允許我們快速地存儲和檢索鍵值對。然而,在并發(fā)場景下使用 map 時,還是有一些問題需要注意的。

本文將探討 Go 語言中的 map 是否是并發(fā)安全的,并提供三種方案來解決并發(fā)問題。

先來回答一下題目的問題,答案就是并發(fā)不安全

看一段代碼示例,當(dāng)兩個 goroutine 同時對同一個 map 進行寫操作時,會發(fā)生什么?

package main

import "sync"

func main() {
    m := make(map[string]int)
    m["foo"] = 1

    var wg sync.WaitGroup
    wg.Add(2)

    go func() {
        for i := 0; i < 1000; i++ {
            m["foo"]++
        }
        wg.Done()
    }()

    go func() {
        for i := 0; i < 1000; i++ {
            m["foo"]++
        }
        wg.Done()
    }()

    wg.Wait()
}

在這個例子中,我們可以看到,兩個 goroutine 將嘗試同時對 map 進行寫入。運行這個程序時,我們將看到一個錯誤:

fatal error: concurrent map writes

也就是說,在并發(fā)場景下,這樣操作 map 是不行的。

為什么是不安全的

因為它沒有內(nèi)置的鎖機制來保護多個 goroutine 同時對其進行讀寫操作。

當(dāng)多個 goroutine 同時對同一個 map 進行讀寫操作時,就會出現(xiàn)數(shù)據(jù)競爭和不一致的結(jié)果。

就像上例那樣,當(dāng)兩個 goroutine 同時嘗試更新同一個鍵值對時,最終的結(jié)果可能取決于哪個 goroutine 先完成了更新操作。這種不確定性可能會導(dǎo)致程序出現(xiàn)錯誤或崩潰。

Go 語言團隊沒有將 map 設(shè)計成并發(fā)安全的,是因為這樣會增加程序的開銷并降低性能。

如果 map 內(nèi)置了鎖機制,那么每次訪問 map 時都需要進行加鎖和解鎖操作,這會增加程序的運行時間并降低性能。

此外,并不是所有的程序都需要在并發(fā)場景下使用 map,因此將鎖機制內(nèi)置到 map 中會對那些不需要并發(fā)安全的程序造成不必要的開銷。

在實際使用過程中,開發(fā)人員可以根據(jù)程序的需求來選擇是否需要保證 map 的并發(fā)安全性,從而在性能和安全性之間做出權(quán)衡。

如何并發(fā)安全

接下來介紹三種并發(fā)安全的方式:

  1. 讀寫鎖
  2. 分片加鎖
  3. sync.Map

加讀寫鎖

第一種方法是使用讀寫鎖,這是最容易想到的一種方式。在讀操作時加讀鎖,在寫操作時加寫鎖。

package main

import (
    "fmt"
    "sync"
)

type SafeMap struct {
    sync.RWMutex
    Map map[string]string
}

func NewSafeMap() *SafeMap {
    sm := new(SafeMap)
    sm.Map = make(map[string]string)
    return sm
}

func (sm *SafeMap) ReadMap(key string) string {
    sm.RLock()
    value := sm.Map[key]
    sm.RUnlock()
    return value
}

func (sm *SafeMap) WriteMap(key string, value string) {
    sm.Lock()
    sm.Map[key] = value
    sm.Unlock()
}

func main() {
    safeMap := NewSafeMap()

    var wg sync.WaitGroup

    // 啟動多個goroutine進行寫操作
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            safeMap.WriteMap(fmt.Sprintf("name%d", i), fmt.Sprintf("John%d", i))
        }(i)
    }

    wg.Wait()

    // 啟動多個goroutine進行讀操作
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            fmt.Println(safeMap.ReadMap(fmt.Sprintf("name%d", i)))
        }(i)
    }

    wg.Wait()
}

在這個示例中,我們定義了一個 SafeMap 結(jié)構(gòu)體,它包含一個 sync.RWMutex 和一個 map[string]string。

定義了兩個方法:ReadMapWriteMap。在 ReadMap 方法中,我們使用讀鎖來保護對 map 的讀取操作。在 WriteMap 方法中,我們使用寫鎖來保護對 map 的寫入操作。

main 函數(shù)中,我們啟動了多個 goroutine 來進行讀寫操作,這些操作都是安全的。

分片加鎖

上例中通過對整個 map 加鎖來實現(xiàn)需求,但相對來說,鎖會大大降低程序的性能,那如何優(yōu)化呢?其中一個優(yōu)化思路就是降低鎖的粒度,不對整個 map 進行加鎖。

這種方法是分片加鎖,將這個 map 分成 n 塊,每個塊之間的讀寫操作都互不干擾,從而降低沖突的可能性。

package main

import (
    "fmt"
    "sync"
)

const N = 16

type SafeMap struct {
    maps  [N]map[string]string
    locks [N]sync.RWMutex
}

func NewSafeMap() *SafeMap {
    sm := new(SafeMap)
    for i := 0; i < N; i++ {
        sm.maps[i] = make(map[string]string)
    }
    return sm
}

func (sm *SafeMap) ReadMap(key string) string {
    index := hash(key) % N
    sm.locks[index].RLock()
    value := sm.maps[index][key]
    sm.locks[index].RUnlock()
    return value
}

func (sm *SafeMap) WriteMap(key string, value string) {
    index := hash(key) % N
    sm.locks[index].Lock()
    sm.maps[index][key] = value
    sm.locks[index].Unlock()
}

func hash(s string) int {
    h := 0
    for i := 0; i < len(s); i++ {
        h = 31*h + int(s[i])
    }
    return h
}

func main() {
    safeMap := NewSafeMap()

    var wg sync.WaitGroup

    // 啟動多個goroutine進行寫操作
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            safeMap.WriteMap(fmt.Sprintf("name%d", i), fmt.Sprintf("John%d", i))
        }(i)
    }

    wg.Wait()

    // 啟動多個goroutine進行讀操作
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            fmt.Println(safeMap.ReadMap(fmt.Sprintf("name%d", i)))
        }(i)
    }

    wg.Wait()
}

在這個示例中,我們定義了一個 SafeMap 結(jié)構(gòu)體,它包含一個長度為 N 的 map 數(shù)組和一個長度為 N 的鎖數(shù)組。

定義了兩個方法:ReadMapWriteMap。在這兩個方法中,我們都使用了一個 hash 函數(shù)來計算 key 應(yīng)該存儲在哪個 map 中。然后再對這個 map 進行讀寫操作。

main 函數(shù)中,我們啟動了多個 goroutine 來進行讀寫操作,這些操作都是安全的。

有一個開源項目 orcaman/concurrent-map 就是通過這種思想來做的,感興趣的同學(xué)可以看看。

sync.Map

最后,在內(nèi)置的 sync 包中(Go 1.9+)也有一個線程安全的 map,通過將讀寫分離的方式實現(xiàn)了某些特定場景下的性能提升。

package main

import (
    "fmt"
    "sync"
)

func main() {
    var m sync.Map
    var wg sync.WaitGroup

    // 啟動多個goroutine進行寫操作
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            m.Store(fmt.Sprintf("name%d", i), fmt.Sprintf("John%d", i))
        }(i)
    }

    wg.Wait()

    // 啟動多個goroutine進行讀操作
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            v, _ := m.Load(fmt.Sprintf("name%d", i))
            fmt.Println(v.(string))
        }(i)
    }

    wg.Wait()
}

有了官方的支持,代碼瞬間少了很多,使用起來方便多了。

在這個示例中,我們使用了內(nèi)置的 sync.Map 類型來存儲鍵值對,使用 Store 方法來存儲鍵值對,使用 Load 方法來獲取鍵值對。

main 函數(shù)中,我們啟動了多個 goroutine 來進行讀寫操作,這些操作都是安全的。

總結(jié)

Go 語言中的 map 本身并不是并發(fā)安全的。

在多個 goroutine 同時訪問同一個 map 時,可能會出現(xiàn)并發(fā)不安全的現(xiàn)象。這是因為 Go 語言中的 map 并沒有內(nèi)置鎖來保護對map的訪問。

盡管如此,我們?nèi)匀豢梢允褂靡恍┓椒▉韺崿F(xiàn) map 的并發(fā)安全。

一種方法是使用讀寫鎖,在讀操作時加讀鎖,在寫操作時加寫鎖。

另一種方法是分片加鎖,將這個 map 分成 n 塊,每個塊之間的讀寫操作都互不干擾,從而降低沖突的可能性。

此外,在內(nèi)置的 sync 包中(Go 1.9+)也有一個線程安全的 map,它通過將讀寫分離的方式實現(xiàn)了某些特定場景下的性能提升。

以上就是本文的全部內(nèi)容,如果覺得還不錯的話歡迎點贊,轉(zhuǎn)發(fā)關(guān)注,感謝支持。


參考文章:

  • https://zhuanlan.zhihu.com/p/356739568

推薦閱讀:文章來源地址http://www.zghlxwxcb.cn/news/detail-460790.html

  • Go 語言切片是如何擴容的?
  • Go 語言數(shù)組和切片的區(qū)別
  • Go 語言 new 和 make 關(guān)鍵字的區(qū)別
  • 為什么 Go 不支持 []T 轉(zhuǎn)換為 []interface
  • 為什么 Go 語言 struct 要使用 tags

到了這里,關(guān)于Go 語言 map 是并發(fā)安全的嗎?的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 使用go_concurrent_map 管理 并發(fā)更新緩存

    在后臺服務(wù)中,為了提速,我在內(nèi)存還做了一個告訴緩存來管理用戶信息,根據(jù)更新通知,或者定時去redis中同步信息,那么在加載或者更新某個用戶元素時,要防止并發(fā), 當(dāng): 1)如果內(nèi)存緩存沒有; 2)去數(shù)據(jù)庫或者redis加載; 3)添加到內(nèi)存緩存; 這里就有個并發(fā)重復(fù)的

    2024年04月25日
    瀏覽(18)
  • 35 | 并發(fā)安全字典sync.Map (下)

    35 | 并發(fā)安全字典sync.Map (下)

    我們在上一篇文章中談到了,由于并發(fā)安全字典提供的方法涉及的鍵和值的類型都是interface{ },所以我們在調(diào)用這些方法的時候,往往還需要對鍵和值的實際類型進行檢查。 這里大致有兩個方案。我們上一篇文章中提到了第一種方案,在編碼時就完全確定鍵和值的類型,然后

    2024年01月24日
    瀏覽(23)
  • 【Go】Go 語言教程--Go 語言Map(集合)(十六)

    往期回顧: Go 語言教程–介紹(一) Go 語言教程–語言結(jié)構(gòu)(二) Go 語言教程–語言結(jié)構(gòu)(三) Go 語言教程–數(shù)據(jù)類型(四) Go 語言教程–語言變量(五) Go 語言教程–GO語言常量(六) Go 語言教程–GO語言運算符(七) Go 語言教程–GO條件和循環(huán)語句(八) Go 語言教程

    2024年02月16日
    瀏覽(23)
  • 掌握Go并發(fā):Go語言并發(fā)編程深度解析

    掌握Go并發(fā):Go語言并發(fā)編程深度解析

    ??? 個人主頁 :鼠鼠我捏,要死了捏的主頁? ??? 系列專欄 :Golang全棧-專欄 ??? 個人學(xué)習(xí)筆記,若有缺誤,歡迎評論區(qū)指正 ? 前些天發(fā)現(xiàn)了一個巨牛的人工智能學(xué)習(xí)網(wǎng)站,通俗易懂,風(fēng)趣幽默,忍不住分享一下給大家。點擊跳轉(zhuǎn)到網(wǎng)站AI學(xué)習(xí)網(wǎng)站。 當(dāng)我們開發(fā)一個W

    2024年02月20日
    瀏覽(24)
  • Golang 中的 map 為什么是并發(fā)不安全的?

    Golang 中的 map 為什么是并發(fā)不安全的?

    ??golang 中的 map 是并發(fā)不安全的,多個 go 協(xié)程同時對同一個 map 進行讀寫操作時,會導(dǎo)致數(shù)據(jù)競爭(data race)問題,程序會 panic。 ??如果一個協(xié)程正在寫入 map,而另一個協(xié)程正在讀取或?qū)懭?map,那么就有可能出現(xiàn)一些未定義的行為,例如:讀取到的值可能是過期的、不

    2024年02月05日
    瀏覽(25)
  • go語言(八)---- map

    go語言(八)---- map

    map的聲明方式有以下三種。 map的使用方式 map的增刪改查 map的傳參

    2024年01月20日
    瀏覽(16)
  • Go 語言Map(集合)

    Map 是一種無序的鍵值對的集合。Map 最重要的一點是通過 key 來快速檢索數(shù)據(jù),key 類似于索引,指向數(shù)據(jù)的值。 Map 是一種集合,所以我們可以像迭代數(shù)組和切片那樣迭代它。不過,Map 是無序的,我們無法決定它的返回順序,這是因為 Map 是使用 hash 表來實現(xiàn)的。 定義 Map 可以

    2024年02月05日
    瀏覽(15)
  • Go語言并發(fā)

    出色的并發(fā)性是Go語言的特色之一 ? 理解并發(fā)與并行 ? 理解進程和線程 ? 掌握Go語言中的Goroutine和channel ? 掌握select分支語句 ? 掌握sync包的應(yīng)用 并發(fā)與并行的概念這里不再贅述, 可以看看之前java版寫的并發(fā)實踐; 程序、進程與線程這里也不贅述 一個進程可以包括多個線

    2024年02月06日
    瀏覽(20)
  • Go 語言 map 如何順序讀取?

    Go 語言中的 map 是一種非常強大的數(shù)據(jù)結(jié)構(gòu),它允許我們快速地存儲和檢索鍵值對。 然而,當(dāng)我們遍歷 map 時,會有一個有趣的現(xiàn)象,那就是輸出的鍵值對順序是不確定的。 先看一段代碼示例: 當(dāng)我們多執(zhí)行幾次這段代碼時,就會發(fā)現(xiàn),輸出的順序是不同的。 首先,Go 語言

    2024年02月07日
    瀏覽(22)
  • Go語言基礎(chǔ)之map

    Go語言中提供的映射關(guān)系容器為map,其內(nèi)部使用散列表(hash)實現(xiàn)。 map是一種無序的基于key-value的數(shù)據(jù)結(jié)構(gòu),Go語言中的map是引用類型,必須初始化才能使用。 map定義 Go語言中 map的定義語法如下: 其中, KeyType:表示鍵的類型。 ValueType:表示鍵對應(yīng)的值的類型。 map類型的變量

    2024年02月11日
    瀏覽(23)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包