searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

go共享map的使用报错fatal error的解决方式

2024-11-13 09:32:05
1
0

在Go语言中,当多个goroutine同时操作一个map时,如果其中一个goroutine在迭代map的同时,另一个goroutine尝试修改这个map(比如添加、删除或更新元素),就会触发fatal error: concurrent map iteration and map write错误。这是因为Go的map在迭代过程中不是线程安全的。

要解决这个问题,你有几个选项:

  1. 使用sync.Map
    Go的sync包提供了一个并发安全的map类型sync.Map。它允许你在多个goroutine之间安全地读写map。不过,请注意sync.Map的API与普通的map有所不同,它提供了StoreLoadDeleteLoadOrStoreRange等方法。

    package main
     
    import (
    	"fmt"
    	"sync"
    )
     
    func main() {
    	var m sync.Map
     
    	// 启动一个goroutine来写入数据
    	go func() {
    		m.Store("key1", "value1")
    		m.Store("key2", "value2")
    	}()
     
    	// 主goroutine迭代数据
    	m.Range(func(key, value interface{}) bool {
    		fmt.Println(key, value)
    		return true // 继续迭代
    	})
     
    	// 注意:这里的代码是简化的,实际使用中需要确保goroutine同步,比如使用sync.WaitGroup
    }
  2. 使用互斥锁(sync.Mutex)
    你可以使用一个互斥锁来保护对map的访问。在迭代map之前,先锁定它;迭代完成后,再解锁。同样,在修改map之前也要先锁定它。这样可以确保在迭代过程中不会有其他goroutine修改map。

    func main() {
    	var m = make(map[string]string)
    	var mu sync.Mutex
    
    	go func() {
    		mu.Lock()
    		m["key1"] = "value1"
    		m["key2"] = "value2"
    		mu.Unlock()
    	}()
     
    	var done sync.WaitGroup
    	done.Add(1)
    	go func() {
    		defer done.Done()
    	}()
    	done.Wait() 
     
    	mu.Lock()
    	for k, v := range m {
    		fmt.Println(k, v)
    	}
    	mu.Unlock()
    
    }
  3. 使用读写锁(sync.RWMutex)
    如果你的应用场景中读操作远多于写操作,那么使用读写锁可能是一个更好的选择。读写锁允许多个读操作同时进行,但写操作会阻塞其他读写操作。这样可以在读操作频繁的情况下提高并发性能。

    package main
    
    import (
    	"fmt"
    	"sync"
    	"time"
    )
    
    func main() {
    	var m = make(map[string]string)
    	var rwmu sync.RWMutex
    
    	// 启动一个goroutine来写入数据
    	go func() {
    		time.Sleep(time.Millisecond * 100) // 模拟写入延迟
    		rwmu.Lock()
    		m["key1"] = "value1"
    		m["key2"] = "value2"
    		rwmu.Unlock()
    	}()
    
    	
    	done := make(chan bool)
    	go func() {
    		time.Sleep(time.Millisecond * 200)
    		done <- true
    	}()
    	<-done // 等待写入完成
    
    	// 迭代数据(读锁)
    	rwmu.RLock()
    	for k, v := range m {
    		fmt.Println(k, v)
    	}
    	rwmu.RUnlock()
    }
  4. 避免在goroutine之间共享map
    如果可能的话,尽量避免在多个goroutine之间共享map。你可以通过为每个goroutine分配一个独立的map来实现这一点,或者使用通道(channel)来传递数据而不是直接共享map。

  5. 重新设计数据结构
    在某些情况下,重新设计数据结构可能是解决并发问题的最佳方法。例如,你可以使用切片(slice)或链表(linked list)等线程安全的数据结构来替代map。

  6. 使用第三方库
    还有一些第三方库提供了并发安全的map实现。这些库可能提供了比sync.Map更丰富的功能或更好的性能。不过,在使用第三方库之前,请确保你已经充分了解了它的工作原理和限制。

  7. 复制数据:
    复制一份新的数据来使用,当然数据量小的情况,如果数据量太大的话,还是要根据实际情况去使用


0条评论
作者已关闭评论
i****g
6文章数
0粉丝数
i****g
6 文章 | 0 粉丝
i****g
6文章数
0粉丝数
i****g
6 文章 | 0 粉丝
原创

go共享map的使用报错fatal error的解决方式

2024-11-13 09:32:05
1
0

在Go语言中,当多个goroutine同时操作一个map时,如果其中一个goroutine在迭代map的同时,另一个goroutine尝试修改这个map(比如添加、删除或更新元素),就会触发fatal error: concurrent map iteration and map write错误。这是因为Go的map在迭代过程中不是线程安全的。

要解决这个问题,你有几个选项:

  1. 使用sync.Map
    Go的sync包提供了一个并发安全的map类型sync.Map。它允许你在多个goroutine之间安全地读写map。不过,请注意sync.Map的API与普通的map有所不同,它提供了StoreLoadDeleteLoadOrStoreRange等方法。

    package main
     
    import (
    	"fmt"
    	"sync"
    )
     
    func main() {
    	var m sync.Map
     
    	// 启动一个goroutine来写入数据
    	go func() {
    		m.Store("key1", "value1")
    		m.Store("key2", "value2")
    	}()
     
    	// 主goroutine迭代数据
    	m.Range(func(key, value interface{}) bool {
    		fmt.Println(key, value)
    		return true // 继续迭代
    	})
     
    	// 注意:这里的代码是简化的,实际使用中需要确保goroutine同步,比如使用sync.WaitGroup
    }
  2. 使用互斥锁(sync.Mutex)
    你可以使用一个互斥锁来保护对map的访问。在迭代map之前,先锁定它;迭代完成后,再解锁。同样,在修改map之前也要先锁定它。这样可以确保在迭代过程中不会有其他goroutine修改map。

    func main() {
    	var m = make(map[string]string)
    	var mu sync.Mutex
    
    	go func() {
    		mu.Lock()
    		m["key1"] = "value1"
    		m["key2"] = "value2"
    		mu.Unlock()
    	}()
     
    	var done sync.WaitGroup
    	done.Add(1)
    	go func() {
    		defer done.Done()
    	}()
    	done.Wait() 
     
    	mu.Lock()
    	for k, v := range m {
    		fmt.Println(k, v)
    	}
    	mu.Unlock()
    
    }
  3. 使用读写锁(sync.RWMutex)
    如果你的应用场景中读操作远多于写操作,那么使用读写锁可能是一个更好的选择。读写锁允许多个读操作同时进行,但写操作会阻塞其他读写操作。这样可以在读操作频繁的情况下提高并发性能。

    package main
    
    import (
    	"fmt"
    	"sync"
    	"time"
    )
    
    func main() {
    	var m = make(map[string]string)
    	var rwmu sync.RWMutex
    
    	// 启动一个goroutine来写入数据
    	go func() {
    		time.Sleep(time.Millisecond * 100) // 模拟写入延迟
    		rwmu.Lock()
    		m["key1"] = "value1"
    		m["key2"] = "value2"
    		rwmu.Unlock()
    	}()
    
    	
    	done := make(chan bool)
    	go func() {
    		time.Sleep(time.Millisecond * 200)
    		done <- true
    	}()
    	<-done // 等待写入完成
    
    	// 迭代数据(读锁)
    	rwmu.RLock()
    	for k, v := range m {
    		fmt.Println(k, v)
    	}
    	rwmu.RUnlock()
    }
  4. 避免在goroutine之间共享map
    如果可能的话,尽量避免在多个goroutine之间共享map。你可以通过为每个goroutine分配一个独立的map来实现这一点,或者使用通道(channel)来传递数据而不是直接共享map。

  5. 重新设计数据结构
    在某些情况下,重新设计数据结构可能是解决并发问题的最佳方法。例如,你可以使用切片(slice)或链表(linked list)等线程安全的数据结构来替代map。

  6. 使用第三方库
    还有一些第三方库提供了并发安全的map实现。这些库可能提供了比sync.Map更丰富的功能或更好的性能。不过,在使用第三方库之前,请确保你已经充分了解了它的工作原理和限制。

  7. 复制数据:
    复制一份新的数据来使用,当然数据量小的情况,如果数据量太大的话,还是要根据实际情况去使用


文章来自个人专栏
it杂谈
6 文章 | 1 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0