go并发访问map的坑 fatal error: concurrent map read and map write

简介: go并发访问map的坑 fatal error: concurrent map read and map write

前言


go 并发访问map是不安全的, 会出现未定义的错误导致程序退出…坑总得踩一踩才会知道


例子


先写一个错误的

package main
import "fmt"
func test() map[string]interface{}{
  taskList := []string{"a", "b"}
  result := make(map[string]interface{})
  for _, task := range taskList {
    go func(task string) {
      switch task {
      case "a":
        result["a"] = "xiaofei"
      case "b":
        result["b"] = "ceshi"
      }
    }(task)
  }
  return result
}
func main() {
  for a := 0; a < 1000; a++ {
    fmt.Println(test())
  }
}

返回结果

image.png


上面代码有两个问题, 第一个是没有阻塞go程, 第二个就是并发访问map了


正确的写法

package main
import (
  "fmt"
  "sync"
)
// 使用读写锁 sync.RWMutex
type NewMap struct {
  lock *sync.RWMutex
  sm   map[interface{}]interface{}
}
func (m *NewMap) Set(k interface{}, v interface{}) bool {
  m.lock.Lock()
  defer m.lock.Unlock()
  m.sm[k] = v
  return true
}
func test() map[interface{}]interface{} {
  taskList := []string{"a", "b"}
  xf := &sync.WaitGroup{}
  result := NewMap{
    lock: new(sync.RWMutex),
    sm: make(map[interface{}]interface{}),
  }
  for _, task := range taskList {
    xf.Add(1)
    go func(task string) {
      defer xf.Done()
      switch task {
      case "a":
        result.Set("a", "xiaofei")
      case "b":
        result.Set("b", "ceshi")
      }
    }(task)
  }
  xf.Wait()
  return result.sm
}
func main() {
  for a := 0; a < 100; a++ {
    fmt.Println(test())
  }
}

建议把这个map做一个基类方法, 所有的方法封装一下直接用就ok了;

目录
相关文章
|
5月前
|
人工智能 安全 算法
Go入门实战:并发模式的使用
本文详细探讨了Go语言的并发模式,包括Goroutine、Channel、Mutex和WaitGroup等核心概念。通过具体代码实例与详细解释,介绍了这些模式的原理及应用。同时分析了未来发展趋势与挑战,如更高效的并发控制、更好的并发安全及性能优化。Go语言凭借其优秀的并发性能,在现代编程中备受青睐。
157 33
|
4月前
|
存储 Go 开发者
Go 语言中如何处理并发错误
在 Go 语言中,并发编程中的错误处理尤为复杂。本文介绍了几种常见的并发错误处理方法,包括 panic 的作用范围、使用 channel 收集错误与结果,以及使用 errgroup 包统一管理错误和取消任务,帮助开发者编写更健壮的并发程序。
88 4
Go 语言中如何处理并发错误
|
3月前
|
存储 人工智能 安全
深入理解 go sync.Map - 基本原理
本文介绍了 Go 语言中 `map` 在并发使用时的常见问题及其解决方案,重点对比了 `sync.Mutex`、`sync.RWMutex` 和 `sync.Map` 的性能差异及适用场景。文章指出,普通 `map` 不支持并发读写,容易引发错误;而 `sync.Map` 通过原子操作和优化设计,在某些场景下能显著提升性能。同时详细讲解了 `sync.Map` 的基本用法及其适合的应用环境,如读多写少或不同 goroutine 操作不同键的场景。
143 1
|
2月前
|
数据采集 Go API
Go语言实战案例:多协程并发下载网页内容
本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
|
2月前
|
数据采集 消息中间件 编解码
Go语言实战案例:使用 Goroutine 并发打印
本文通过简单案例讲解 Go 语言核心并发模型 Goroutine,涵盖协程启动、输出控制、主程序退出机制,并结合 sync.WaitGroup 实现并发任务同步,帮助理解 Go 并发设计思想与实际应用。
|
5月前
|
存储 安全 Go
Map的遍历与判断键是否存在-《Go语言实战指南》
本文介绍了 Go 语言中对 `map` 的常见操作,包括遍历所有项和判断键是否存在。通过 `for range` 可以遍历 `map` 的键值对、仅键或仅值(需忽略键)。注意,`map` 遍历顺序是随机的。判断键是否存在时,使用双赋值语法 `value, ok := map[key]`,其中 `ok` 表示键是否存在。直接访问不存在的键会返回类型的零值,可能导致逻辑错误。掌握这些机制可更安全高效地处理键值对数据。
|
6月前
|
数据采集 监控 Go
用 Go 实现一个轻量级并发任务调度器(支持限速)
本文介绍了如何用 Go 实现一个轻量级的并发任务调度器,解决日常开发中批量任务处理的需求。调度器支持最大并发数控制、速率限制、失败重试及结果收集等功能。通过示例代码展示了其使用方法,并分析了核心组件设计,包括任务(Task)和调度器(Scheduler)。该工具适用于网络爬虫、批量请求等场景。文章最后总结了 Go 并发模型的优势,并提出了扩展功能的方向,如失败回调、超时控制等,欢迎读者交流改进。
197 25
|
8月前
|
存储 缓存 安全
Go 语言中的 Sync.Map 详解:并发安全的 Map 实现
`sync.Map` 是 Go 语言中用于并发安全操作的 Map 实现,适用于读多写少的场景。它通过两个底层 Map(`read` 和 `dirty`)实现读写分离,提供高效的读性能。主要方法包括 `Store`、`Load`、`Delete` 等。在大量写入时性能可能下降,需谨慎选择使用场景。
|
9月前
|
存储 安全 Go
Go语言中的map数据结构是如何实现的?
Go 语言中的 `map` 是基于哈希表实现的键值对数据结构,支持快速查找、插入和删除操作。其原理涉及哈希函数、桶(Bucket)、动态扩容和哈希冲突处理等关键机制,平均时间复杂度为 O(1)。为了确保线程安全,Go 提供了 `sync.Map` 类型,通过分段锁实现并发访问的安全性。示例代码展示了如何使用自定义结构体和切片模拟 `map` 功能,以及如何使用 `sync.Map` 进行线程安全的操作。
238 9
|
10月前
|
Go
go语言for遍历映射(map)
go语言for遍历映射(map)
343 12