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.