Golang 程序实现并发哈希映射

go programmingserver side programmingprogramming

并发哈希映射可以被视为确保平稳并行执行的高效数据结构。它旨在高效处理并发读写操作,是构建高性能多线程应用程序的宝贵工具。在本文中,我们学习在 Go 中实现并发哈希映射。

语法

func NewConcurrentMap(size int) *ConcurrentMap

语法定义了一个名为 NewConcurrentMap 的函数,用于创建并返回名为 ConcurrentMap 的自定义并发哈希映射的实例。它采用大小参数来确定数据分发的内部存储桶数,并封装初始化过程。

算法

  • 首先初始化一个固定大小的数组作为哈希映射的底层存储。

  • 实现一个哈希函数,用于将键映射到数组内的索引。

  • 对于每个索引,我们创建单独的链接列表以避免冲突。

  • 定义添加、检索和删除键值对的方法,同时确保同步。

  • 为了使 goroutines 能够同时访问哈希映射的各个部分,最后实现锁定机制,例如互斥锁。

示例 1

在此示例中,为了在 go 中实现并发哈希映射,我们使用 sync.Map() 包和 goroutines,我们直接构建一个并发映射。然后,我们使用空的并发映射,使用 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 的个性化程度。

结论

并发哈希映射是现代并行编程中的关键数据结构,允许多个线程同时访问和修改数据,同时保持同步。在本文中,我们通过两种不同的方法详细阐述了实现并发哈希映射的概念。第一种方法使用内置的 sync.Map 类型动态处理同步,并且对于涉及多个线程或 goroutine 的场景非常有效。下一种自定义实现方法提供了对同步的细粒度控制,允许显式管理并发并提供对同步过程的洞察。


相关文章