博客主页:🏆看看是李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并发安全文章来源:https://www.toymoban.com/news/detail-434773.html
(43条消息) GoLang之切片并发安全问题_GoGo在努力的博客-CSDN博客_golang 切片线程安全文章来源地址https://www.toymoban.com/news/detail-434773.html
到了这里,关于go中数组、切片、map是否线程(并发)安全?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!