go中数组、切片、map是否线程(并发)安全?

博客主页:🏆看看是李XX还是李歘歘 🏆

🌺每天不定期分享一些包括但不限于计算机基础、算法、后端开发相关的知识点,以及职场小菜鸡的生活。🌺

💗点关注不迷路,总有一些📖知识点📖是你想要的💗 

目录

什么是线程(并发)安全?

非线程安全原因

map

解决方案

数组

解决方案

切片

解决方案

Go其他数据类型的并发安全性


先给出结论:在Go中数组、切片和map都是非线程安全的。

什么是线程(并发)安全?

线程(并发)安全是指程序在并发执行或者多个线程同时操作的情况下,执行结果还是正确的。

非线程安全原因

map

Go语言中的 map 在并发情况下,只读是线程安全的,同时读写是线程不安全的。同一个变量在多个goroutine中访问需要保证其安全性

因为map变量为指针类型变量,并发写时,多个协程同时操作一个内存,类似于多线程操作同一个资源会发生竞争关系,共享资源会遭到破坏,因此golang出于安全的考虑,抛出致命错误:fatal error: concurrent map writes

 非并发安全map(普通的map)

package main

import (
	"fmt"
	"strconv"
	"sync"
)

var m = make(map[string]int)

func get(key string) int {
	return m[key]
}

func set(key string, value int) {
	m[key] = value
}

func main() {
	wg := sync.WaitGroup{}
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(n int) {
			key := strconv.Itoa(n)
			set(key, n)
			fmt.Printf("k=:%v,v:=%v\n", key, get(key))
			wg.Done()
		}(i)
	}
	wg.Wait()
}

解决方案

(1)在写操作的时候增加锁

package main

import (
	"fmt"
	"sync"
)

func main() {
	var lock sync.Mutex
	var maplist map[string]int
	maplist = map[string]int{"one": 1, "two": 2}
	lock.Lock()
	maplist["three"] = 3
	lock.Unlock()
	fmt.Println(maplist)
}

(2)sync.Map包

package main

import (
	"fmt"
	"sync"
)

func main() {
	m := sync.Map{} //或者 var mm sync.Map
	m.Store("a", 1)
	m.Store("b", 2)
	m.Store("c", 3)                             //插入数据
	fmt.Println(m.Load("a"))                    //读取数据
	m.Range(func(key, value interface{}) bool { //遍历
		fmt.Println(key, value)
		return true
	})
}

数组

指定索引进行读写时,数组是支持并发读写索引区的数据的,但是索引区的数据在并发时会被覆盖的;

解决方案

1.加锁

2.控制并发顺序

切片

指定索引进行读写:是支持并发读写索引区的数据的,但是索引区的数据在并发时可能会被覆盖的;

发生切片动态扩容:并发场景下扩容可能会被覆盖。

切片是对数组的抽象,其底层就是数组,在并发下写数据到相同的索引位会被覆盖,并且切片也有自动扩容的功能,当切片要进行扩容时,就要替换底层的数组,在切换底层数组时,多个goroutine是同时运行的,哪个goroutine先运行是不确定的,不论哪个goroutine先写入内存,肯定就有一次写入会覆盖之前的写入,所以在动态扩容时并发写入数组是不安全的;

解决方案

1.加互斥锁
2.使用channel串行化操作
3.使用sync.map代替切片(github上著名的iris框架也曾遇到过切片动态扩容导致webscoket连接数减少的bug,最终采用sync.map解决了该问题, 采用sync.map解决切片并发安全)

Go其他数据类型的并发安全性

数据类型参考:

Go 中所有类型并发赋值的安全性。

(1)由一条机器指令完成赋值的类型并发赋值是安全的,这些类型有:字节型,布尔型、整型、浮点型、字符型、指针、函数。

(2)数组由一个或多个元素组成,大部分情况并发不安全。注意:当位宽不大于 64 位且是 2 的整数次幂(8,16,32,64),那么其并发赋值是安全的。

(3)struct 或底层是 struct 的类型并发赋值大部分情况并发不安全,这些类型有:复数、字符串、 数组、切片、映射、通道、接口。注意:当 struct 赋值时退化为单个字段由一个机器指令完成赋值时,并发赋值又是安全的。这种情况有:
(a)实部或虚部相同的复数的并发赋值;
(b)等长字符串的并发赋值;
(c)同长度同容量切片的并发赋值;
(d)同一种具体类型不同值并发赋给接口。
————————————————
版权声明:本文为CSDN博主「恋喵大鲤鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/K346K346/article/details/115099353

这篇文章写的很好,感谢博主,但是底层是 struct 的类型的channel是并发安全的,还有待博主回复

参考:

(43条消息) 【golang学习笔记】Go语言中参数的传递是值传递还是引用传递_Vivien_oO0的博客-CSDN博客_golang 切片是值传递还是引用传递

(43条消息) Go并发安全sync.Map_JIeJaitt的博客-CSDN博客

(43条消息) golang-数组,切片,map是否线程安全?_golang 切片线程安全_一颗简单的心的博客-CSDN博客 (43条消息) Go语言map使用和并发安全_行走的皮卡丘的博客-CSDN博客_go map并发安全

(43条消息) GoLang之切片并发安全问题_GoGo在努力的博客-CSDN博客_golang 切片线程安全

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值