Go语言中的map为什么默认不是并发安全的?

简介: Go语言的map默认不保证并发安全,以优化性能和简洁性。官方建议在需要时使用`sync.Mutex`保证安全。从Go 1.6起,并发读写map会导致程序崩溃,鼓励开发者显式处理并发问题。这样做的哲学是让代码更清晰,并避免不必要的性能开销。

今天我们聊一个 Go 语言中的 “热门” 话题——为什么 Go 语言中的 map 默认不是并发安全的呢?

对于广大 Go 程序员来说,尤其是那些刚跨入 Go 世界的新朋友们,这个问题或许让你们摸不着头脑。别急,让我们一起慢慢揭开这层神秘的面纱。

Go 语言中 map 的基本使用

首先,我们得知道 map 是什么。在 Go 中,map 是一种内置的数据结构,它提供了 “键值对”(Key-Value)的存储机制。使用 map,你可以通过 Key 快速找到对应的 Value,这让我们在处理一些需要快速查询的场景时如虎添翼。

一个简单的 map 示例:

package main

import "fmt"

func main() {
   
    // 创建一个map
    myMap := make(map[string]int)
    // 向map中添加键值对
    myMap["apple"] = 1
    myMap["banana"] = 2

    // 从map中获取值
    fmt.Println(myMap["apple"]) // 输出: 1
}

那为什么 map 默认不是并发安全的呢?

难不成 Go 官方觉得太复杂了?性能太差了?还是为了什么?

典型使用场景

Go 官方认为,map 的典型使用场景并不需要从多个 goroutine 中安全地访问。因此,在设计时,优先考虑了性能和简单性,而没有将并发安全作为默认特性。这是一种基于使用案例进行权衡的结果。

性能考量

引入并发安全意味着每次操作 map 时都需要进行加锁和解锁,这无疑会增加额外的性能开销。为了大多数程序的性能考虑,Go 没有将 map 设计为并发安全的,因为这会导致即使在不需要并发访问的场景下,也要付出不必要的性能代价。

官方方案

Go 1.6 开始,引入了并发访问 map 的检测机制,如果检测到并发读写,程序会直接崩溃,而不是隐瞒问题。Go 官方倾向于让问题显露出来("let it crash"),这样可以迫使开发者正视并发问题,采取正确的方法来解决。

如何安全地在多个 goroutine 中操作 map?

虽然原生的 map 不是并发安全的,但 Go 提供了其他机制来解决并发访问的问题。最直接的方法是使用互斥锁 sync.Mutex,来确保同一时间只有一个 goroutine 能访问 map。

当然现在不止这一个方法保证 map 并发安全,由于篇幅有限,这里仅以此为例。

例子如下:

package main

import (
    "fmt"
    "sync"
)

var (
    myMap = make(map[string]int)
    lock  sync.Mutex
)

func main() {
   
    // 启动一个 goroutine 写入数据
    go func() {
   
        for {
   
            lock.Lock()
            myMap["apple"] = 1
            lock.Unlock()
        }
    }()

    // 在主 goroutine 中读取数据
    for {
   
        lock.Lock()
        fmt.Println(myMap["apple"])
        lock.Unlock()
    }
}

结论

通过以上的探讨,我们了解了为什么 Go 语言中的 map 默认不是并发安全的,其实就是一句话概括:Go 官方觉得大部分场景都不需要支持并发,从性能上做的考虑

Go 语言的设计哲学之一就是简单而有效,通过让开发者显式地处理并发问题,既保证了性能,也让代码的行为更加透明。

也有网友讨论说,可以像 Java 那样提供两个 map,一个支持并发,性能差些,一个不支持并发,性能好。但是 Go 官方为什么不提供两个,那就不得而知了,可能是为了符合 Go 语言“少就是多”的理念?

你有什么看法呢?一起聊聊……

相关文章
|
5天前
|
Go 调度 开发者
探索Go语言中的并发模式:goroutine与channel
在本文中,我们将深入探讨Go语言中的核心并发特性——goroutine和channel。不同于传统的并发模型,Go语言的并发机制以其简洁性和高效性著称。本文将通过实际代码示例,展示如何利用goroutine实现轻量级的并发执行,以及如何通过channel安全地在goroutine之间传递数据。摘要部分将概述这些概念,并提示读者本文将提供哪些具体的技术洞见。
|
16天前
|
并行计算 安全 Go
Go语言的并发特性
【10月更文挑战第26天】Go语言的并发特性
8 1
|
21天前
|
Java 大数据 Go
Go语言:高效并发的编程新星
【10月更文挑战第21】Go语言:高效并发的编程新星
44 7
|
26天前
|
安全 Go 调度
探索Go语言的并发模式:协程与通道的协同作用
Go语言以其并发能力闻名于世,而协程(goroutine)和通道(channel)是实现并发的两大利器。本文将深入了解Go语言中协程的轻量级特性,探讨如何利用通道进行协程间的安全通信,并通过实际案例演示如何将这两者结合起来,构建高效且可靠的并发系统。
|
24天前
|
安全 程序员 Go
深入浅出Go语言的并发之道
在本文中,我们将探索Go语言如何优雅地处理并发编程。通过对比传统多线程模型,我们将揭示Go语言独特的goroutine和channel机制是如何简化并发编程,并提高程序的效率和稳定性。本文不涉及复杂的技术术语,而是用通俗易懂的语言,结合生动的比喻,让读者能够轻松理解Go语言并发编程的核心概念。
|
2月前
|
Go 定位技术 索引
Go 语言Map(集合) | 19
Go 语言Map(集合) | 19
|
2月前
|
存储 前端开发 API
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
该文章详细介绍了ES6中Set和Map数据结构的特性和使用方法,并探讨了它们在前端开发中的具体应用,包括如何利用这些数据结构来解决常见的编程问题。
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
|
1月前
|
存储 分布式计算 Java
Stream很好,Map很酷,但答应我别用toMap():Java开发中的高效集合操作
在Java的世界里,Stream API和Map集合无疑是两大强大的工具,它们极大地简化了数据处理和集合操作的复杂度。然而,在享受这些便利的同时,我们也应当警惕一些潜在的陷阱,尤其是当Stream与Map结合使用时。本文将深入探讨Stream与Map的优雅用法,并特别指出在使用toMap()方法时需要注意的问题,旨在帮助大家在工作中更高效、更安全地使用这些技术。
40 0
|
3月前
|
存储 安全 Java
java集合框架复习----(4)Map、List、set
这篇文章是Java集合框架的复习总结,重点介绍了Map集合的特点和HashMap的使用,以及Collections工具类的使用示例,同时回顾了List、Set和Map集合的概念和特点,以及Collection工具类的作用。
java集合框架复习----(4)Map、List、set
|
3月前
|
Java
【Java集合类面试二十二】、Map和Set有什么区别?
该CSDN博客文章讨论了Map和Set的区别,但提供的内容摘要并未直接解释这两种集合类型的差异。通常,Map是一种键值对集合,提供通过键快速检索值的能力,而Set是一个不允许重复元素的集合。