4 回复

请展示在main函数中如何实现?比如一个写入和读取的示例

更多关于Golang中如何安全地实现map的并发读写的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我创建了一个封装互斥锁和映射的结构体,这样可以清晰地体现它们之间的关系

type MyMap struct {
	sync.RWMutex
	internal map[string]int
}

func (m *MyMap) read(key string) (int, boolean) {
	m.RLock()
	result, ok := m.internal[key]
	m.RUnlock()
	return result, ok
}

func (m *MyMap) write(key string, value int) {
	m.Lock()
	m.internal[key] = value
	m.Unlock()
}
  1. 将映射声明更改为

    var m *MyMap
    
  2. 在主函数中初始化 m

    m = &MyMap{
    		internal: map[string]int{
    			"justin": 19,
    			"james":  28,
    			"juliet": 20,
    		},
    	}
    
  3. 将读取和写入协程更改为

    func read() {
      if r, ok := m.read("justin"); ok {
          fmt.Println(r)
      }
    }
    
    func write() {
      m.write("justin", 20)
      fmt.Println(m.internal)
    }
    

在Go语言中,map本身不是并发安全的,直接并发读写会导致运行时panic。你的代码尝试使用sync.Mutex来保护map的并发访问,这是一个正确的方向,但实现上存在一些问题。以下是一个改进后的安全并发读写map的示例,使用sync.RWMutex来优化读写性能:

package main

import (
	"fmt"
	"sync"
)

type SafeMap struct {
	mu   sync.RWMutex
	data map[string]int
}

func NewSafeMap() *SafeMap {
	return &SafeMap{
		data: make(map[string]int),
	}
}

func (m *SafeMap) Set(key string, value int) {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.data[key] = value
}

func (m *SafeMap) Get(key string) (int, bool) {
	m.mu.RLock()
	defer m.mu.RUnlock()
	value, exists := m.data[key]
	return value, exists
}

func (m *SafeMap) Delete(key string) {
	m.mu.Lock()
	defer m.mu.Unlock()
	delete(m.data, key)
}

func main() {
	safeMap := NewSafeMap()
	var wg sync.WaitGroup

	// 并发写入
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			key := fmt.Sprintf("key%d", i)
			safeMap.Set(key, i)
		}(i)
	}

	// 并发读取
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			key := fmt.Sprintf("key%d", i)
			if value, exists := safeMap.Get(key); exists {
				fmt.Printf("Key: %s, Value: %d\n", key, value)
			}
		}(i)
	}

	wg.Wait()
}

这个实现有以下改进:

  1. 使用sync.RWMutex替代sync.Mutex,允许多个goroutine同时读取map
  2. 封装了map操作的方法,确保所有访问都通过mutex保护
  3. 使用defer语句确保锁一定会被释放
  4. 提供了完整的Set、Get、Delete操作方法

对于更高性能的场景,可以考虑使用sync.Map,它针对特定并发模式进行了优化:

package main

import (
	"fmt"
	"sync"
)

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

	// 并发写入
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			key := fmt.Sprintf("key%d", i)
			sm.Store(key, i)
		}(i)
	}

	// 并发读取
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			key := fmt.Sprintf("key%d", i)
			if value, ok := sm.Load(key); ok {
				fmt.Printf("Key: %s, Value: %d\n", key, value)
			}
		}(i)
	}

	wg.Wait()
}

选择哪种方案取决于具体的使用场景:对于读写比例较高的场景,使用sync.RWMutex的自定义结构通常性能更好;对于键值对很少变化且主要进行读取的场景,sync.Map可能更合适。

回到顶部