Go語言程式實現併發雜湊對映


併發雜湊對映可以被認為是用於確保平滑和並行執行的高效資料結構。它旨在有效地處理併發讀寫操作,是構建高效能多執行緒應用程式的寶貴工具。在本文中,我們將學習如何在 Go 中實現併發雜湊對映。

語法

func NewConcurrentMap(size int) *ConcurrentMap

語法定義了一個名為 NewConcurrentMap 的函式,該函式用於建立並返回一個名為 ConcurrentMap 的自定義併發雜湊對映的例項。它接受一個 size 引數來確定資料分佈的內部桶數,並封裝了初始化過程。

演算法

  • 首先初始化一個固定大小的陣列作為雜湊對映的基礎儲存。

  • 實現一個雜湊函式,用於將鍵對映到陣列中的索引。

  • 對於每個索引,我們建立單獨的連結串列來避免衝突。

  • 定義新增、檢索和刪除鍵值對的方法,同時確保同步。

  • 為了使 goroutine 能夠同時訪問雜湊對映的部分內容,最後實現一個鎖定機制,例如互斥鎖。

示例 1

在本例中,為了在 Go 中實現併發雜湊對映,我們使用 sync.Map() 包和 goroutine,直接構建一個併發對映。然後,我們使用空的併發對映透過 goroutine 併發插入鍵值對,但也同時使用同步來確保資料一致性。然後,我們使用併發 goroutine 從對映中檢索與鍵關聯的值,以演示執行緒安全訪問。

package main
import (
    "fmt"
	"sync"
)
func main() {
	concurrentMap := &sync.Map{}
	fmt.Println("Step 1: Concurrent Map Created")
    fmt.Println()
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
    	go func(index int) {
        	defer wg.Done()
        	key := fmt.Sprintf("key%d", index)
        	value := fmt.Sprintf("value%d", index)
        	concurrentMap.Store(key, value)
            fmt.Printf("Step 2: Inserted Key: %s, Value: %s\n", key, value)
    	}(i)
    }
	wg.Wait()
    fmt.Println()
    for i := 0; i < 5; i++ {
        wg.Add(1)
    	go func(index int) {
        	defer wg.Done()
        	key := fmt.Sprintf("key%d", index)
            if value, exists := concurrentMap.Load(key); exists {
            	fmt.Printf("Step 3: Retrieved Key: %s, Value: %s\n", key, value)
        	}
        }(i)
    }
    wg.Wait()
}

輸出

Step 1: Concurrent Map Created

Step 2: Inserted Key: key4, Value: value4
Step 2: Inserted Key: key0, Value: value0
Step 2: Inserted Key: key1, Value: value1
Step 2: Inserted Key: key2, Value: value2
Step 2: Inserted Key: key3, Value: value3

Step 3: Retrieved Key: key4, Value: value4
Step 3: Retrieved Key: key0, Value: value0
Step 3: Retrieved Key: key1, Value: value1
Step 3: Retrieved Key: key2, Value: value2
Step 3: Retrieved Key: key3, Value: value3

示例 2

在本例中,我們新引入了一個自定義的併發雜湊對映實現,即 ConcurrentMap 型別,每個桶都有單獨的互斥鎖來維護同步。定義的 NewConcurrentMap 函式使用給定的桶數和相應的互斥鎖初始化對映。然後,使用計算出的桶索引以及 Insert 和 Get 方法來控制對對映的訪問,鎖定和解鎖關聯的互斥鎖,確保執行緒安全的插入和檢索。

package main
import (
        	"fmt"
        	"sync"
)
type ConcurrentMap struct {
	buckets []map[string]string
	mutexes []sync.Mutex
}
func NewConcurrentMap(size int) *ConcurrentMap {
	cm := &ConcurrentMap{make([]map[string]string, size), make([]sync.Mutex, size)}
	for i := range cm.buckets {
      	cm.buckets[i] = make(map[string]string)
    }
	fmt.Println("Step 1: Custom Concurrent Map Created")
    return cm
}
func (cm *ConcurrentMap) Insert(key, value string) {
	index := hash(key) % len(cm.buckets)
    cm.mutexes[index].Lock()
	defer cm.mutexes[index].Unlock()
	cm.buckets[index][key] = value
    fmt.Printf("Step 2: Inserted Key: %s, Value: %s\n", key, value)
}
func (cm *ConcurrentMap) Get(key string) (string, bool) {
	index := hash(key) % len(cm.buckets)
	cm.mutexes[index].Lock()
	defer cm.mutexes[index].Unlock()
	val, exists := cm.buckets[index][key]
	return val, exists
}
func hash(key string) int {
	return len(key)
}
func main() {
	concurrentMap := NewConcurrentMap(10)
	fmt.Println()
	var wg sync.WaitGroup
	for i := 0; i < 5; i++ {
      	wg.Add(1)
    	go func(index int) { defer wg.Done(); key, value := fmt.Sprintf("key%d", index), fmt.Sprintf("value%d", index); concurrentMap.Insert(key, value) }(i)
	}
    wg.Wait()
    fmt.Println()
	for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(index int) { defer wg.Done(); key := fmt.Sprintf("key%d", index); if value, exists := concurrentMap.Get(key); exists { fmt.Printf("Step 3: Retrieved Key: %s, Value: %s\n", key, value) } }(i)
    }
    wg.Wait()
}

輸出

Step 1: Custom Concurrent Map Created

Step 2: Inserted Key: key4, Value: value4
Step 2: Inserted Key: key0, Value: value0
Step 2: Inserted Key: key1, Value: value1
Step 2: Inserted Key: key2, Value: value2
Step 2: Inserted Key: key3, Value: value3

Step 3: Retrieved Key: key4, Value: value4
Step 3: Retrieved Key: key0, Value: value0
Step 3: Retrieved Key: key1, Value: value1
Step 3: Retrieved Key: key2, Value: value2
Step 3: Retrieved Key: key3, Value: value3

現實生活中的應用

  • 剽竊檢測:雜湊對映用於剽竊檢測系統,用於儲存從文件中提取的短語或文字塊的雜湊值。此功能允許輕鬆比較和識別相同或無法區分的資料,有助於檢測涉嫌剽竊的例項。

  • 社交媒體關注者系統:考慮一個類似 Instagram 的社交媒體網路。該平臺使用雜湊對映來管理與每個使用者關聯的關注者和被關注者。點選使用者資料上的“關注”按鈕,會立即更新雜湊對映,在唯一的使用者 ID 和選擇關注的人的使用者 ID 之間建立連線。這使得輕鬆檢索一個人的關注者以及被該人關注的人成為可能,從而提升了 Feed 個性化。

結論

併發雜湊對映是現代並行程式設計中一個至關重要的資料結構,它允許多個執行緒同時訪問和修改資料,同時保持同步。在本文中,我們透過兩種不同的方法詳細闡述了在 Go 中實現併發雜湊對映的概念。第一種方法使用內建的 sync.Map 型別動態處理同步,對於涉及多個執行緒或 goroutine 的場景非常有效。第二種自定義實現方法提供了對同步的細粒度控制,允許顯式管理併發並深入瞭解同步過程。

更新於: 2023年10月18日

405 次檢視

開啟你的 職業生涯

透過完成課程獲得認證

開始學習
廣告

© . All rights reserved.