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

深入探討Go語言中的map是否是并發(fā)安全以及解決方法

 更新時間:2023年05月29日 08:29:12   作者:yongxinz  
這篇文章主要來和大家探討?Go?語言中的?map?是否是并發(fā)安全的,并提供三種方案來解決并發(fā)問題,文中的示例代碼講解詳細,需要的可以參考一下

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

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

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

看一段代碼示例,當兩個 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 是不行的。

為什么是不安全的

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

當多個 goroutine 同時對同一個 map 進行讀寫操作時,就會出現數據競爭和不一致的結果。

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

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

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

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

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

如何并發(fā)安全

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

  • 讀寫鎖
  • 分片加鎖
  • 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 結構體,它包含一個 sync.RWMutex 和一個 map[string]string

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

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

分片加鎖

上例中通過對整個 map 加鎖來實現需求,但相對來說,鎖會大大降低程序的性能,那如何優(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 結構體,它包含一個長度為 N 的 map 數組和一個長度為 N 的鎖數組。

定義了兩個方法:ReadMap 和 WriteMap。在這兩個方法中,我們都使用了一個 hash 函數來計算 key 應該存儲在哪個 map 中。然后再對這個 map 進行讀寫操作。

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

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

sync.Map

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

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()
}

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

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

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

總結

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

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

盡管如此,我們仍然可以使用一些方法來實現 map 的并發(fā)安全。

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

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

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

到此這篇關于深入探討Go語言中的map是否是并發(fā)安全以及解決方法的文章就介紹到這了,更多相關Go語言map內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 一文詳解GO如何實現Redis的AOF持久化

    一文詳解GO如何實現Redis的AOF持久化

    這篇文章主要為大家詳細介紹了GO如何實現Redis的AOF持久化的,文中的示例代碼講解詳細,具有一定的借鑒價值,感興趣的小伙伴可以了解一下
    2023-03-03
  • 利用Go語言搭建WebSocket服務端方法示例

    利用Go語言搭建WebSocket服務端方法示例

    這篇文章主要給大家介紹了利用Go語言搭建WebSocket服務端方法,文中通過示例代碼介紹的非常詳細,需要的朋友們可以參考借鑒,下面來一起看看吧。
    2017-04-04
  • golang操作rocketmq的示例代碼

    golang操作rocketmq的示例代碼

    這篇文章主要介紹了golang操作rocketmq的示例代碼,代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-04-04
  • Golang中map的深入探究

    Golang中map的深入探究

    Go中Map是一個KV對集合,下面這篇文章主要給大家介紹了關于Golang中map探究的相關資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2022-09-09
  • 一文帶你了解Golang中的并發(fā)性

    一文帶你了解Golang中的并發(fā)性

    并發(fā)是一個很酷的話題,一旦你掌握了它,就會成為一筆巨大的財富。所以本文就來和大家一起來聊聊Golang中的并發(fā)性,感興趣的可以了解一下
    2023-03-03
  • 在golang中使用Sync.WaitGroup解決等待的問題

    在golang中使用Sync.WaitGroup解決等待的問題

    這篇文章主要介紹了在golang中使用Sync.WaitGroup解決等待的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 詳解如何在Golang中執(zhí)行shell命令

    詳解如何在Golang中執(zhí)行shell命令

    這篇文章主要為大家詳細介紹了在 golang 中執(zhí)行 shell 命令的多種方法和場景,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-02-02
  • Go標準庫Flag庫和Log庫的使用

    Go標準庫Flag庫和Log庫的使用

    本文主要介紹了Go標準庫Flag庫和Log庫的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-05-05
  • go語言接口之接口值舉例詳解

    go語言接口之接口值舉例詳解

    接口是一種抽象類型,是對其他類型行為的概括與抽象,從語法角度來看,接口是一組方法定義的集合,下面這篇文章主要給大家介紹了關于go語言接口之接口值的相關資料,文章通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-06-06
  • golang實現對JavaScript代碼混淆

    golang實現對JavaScript代碼混淆

    在Go語言中,你可以使用一些工具來混淆JavaScript代碼,一個常用的工具是Terser,它可以用于壓縮和混淆JavaScript代碼,你可以通過Go語言的`os/exec`包來調用Terser工具,本文給通過一個簡單的示例給大家介紹一下,感興趣的朋友可以參考下
    2024-01-01

最新評論