golang 并发锁的陷阱
错误代码示例
package main
import (
"sync"
"strconv"
"fmt"
)
type Node struct {
sync.Mutex
Data map[string]string
}
var Cache []Node;
func main() {
Cache = make([]Node, 2);
Cache[0] = Node{Data : make(map[string]string)}
Cache[1] = Node{Data : make(map[string]string)}
wg := sync.WaitGroup{}
for i := 0; i < 10000; i++ {
wg.Add(1)
go func (index int) {
defer wg.Done()
j := index % 2
node := Cache[j]
node.Lock()
defer node.Unlock()
node.Data[strconv.Itoa(index)] = strconv.Itoa(index)
}(i)
}
wg.Wait();
fmt.Println(Cache[0])
}
看上面这块代码逻辑很简单,并发10000个协程对Cache中的Data进行赋值,偶数index就赋值到第0个map,奇数就赋值第1个map,并且map赋值的时候都加了锁,但是在golang 1.8 运行的时候会爆出如下错误
fatal error: concurrent map writes
fatal error: concurrent map writes
goroutine 26 [running]:
runtime.throw(0x10b4392, 0x15)
......
为什么加锁了仍然会报cuncurrent map wirtes,这一定是golang 1.8 的bug(开玩笑的……)!
错误原因
主要原因是golang的struct 在赋值的时候是进行浅拷贝,把结构体的成员进行了copy,Node 结构体有两个成员
type Node struct {
sync.Mutex
Data map[string]string
}
我们从slice中把Node拿出来的时候,其实是copy了一份Node,Map是指针类型的,所以多份copy其实是操作一份map,但是sync.Mutex类型是struct,他进行了一次copy
所以在每个协程中取出来的时候,Mutex都进行了一次copy,Lock的时候不是同一份锁,所以会出现并发map写入。
解决方法1
把Node的成员Mutex 改成指针类型,那么在copy的时候,mutex 能保持对同一份进行Lock,代码如下
package main
import (
"fmt"
"strconv"
"sync"
)
type Node struct {
*sync.Mutex
Data map[string]string
}
var Cache []Node
func main() {
Cache = make([]Node, 2)
Cache[0] = Node{Data: make(map[string]string), Mutex: &sync.Mutex{}}
Cache[1] = Node{Data: make(map[string]string), Mutex: &sync.Mutex{}}
wg := sync.WaitGroup{}
for i := 0; i < 10000; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
j := index % 2
node := Cache[j]
node.Lock()
defer node.Unlock()
node.Data[strconv.Itoa(index)] = strconv.Itoa(index)
}(i)
}
wg.Wait()
fmt.Println(Cache[0])
}
Mutex 改成指针类型即可保证同一份锁。
解决方法2 Cache中存储Node指针
Cache中如果是Node指针类型,那么index访问的时候,拿出来是指针的副本,指向的仍然是同一份地址,加锁的时候仍然访问的是同一份资源
代码如下
package main
import (
"fmt"
"strconv"
"sync"
)
type Node struct {
sync.Mutex
Data map[string]string
}
var Cache []*Node
func main() {
Cache = make([]*Node, 2)
Cache[0] = &Node{Data: make(map[string]string)}
Cache[1] = &Node{Data: make(map[string]string)}
//fmt.Println(Cache);return;
wg := sync.WaitGroup{}
for i := 0; i < 10000; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
j := index % 2
node := Cache[j]
node.Lock()
defer node.Unlock()
node.Data[strconv.Itoa(index)] = strconv.Itoa(index)
}(i)
}
wg.Wait()
fmt.Println(Cache[0])
}
总结
golang 类似于C++,系统提供的赋值都是浅拷贝,如果确认需要对同一份内容进行访问的时候,需要在特定的地方用上指针
golang 并发锁的陷阱的更多相关文章
- Golang - 并发编程
目录 Golang - 并发编程 1. 并行和并发 2. go语言并发优势 3. goroutine是什么 4. 创建goroutine 5. runtime包 6. channel是什么 7. ch ...
- Redis修改数据多线程并发—Redis并发锁
本文版权归博客园和作者本人吴双共同所有 .转载爬虫请注明地址,博客园蜗牛 http://www.cnblogs.com/tdws/p/5712835.html 蜗牛Redis系列文章目录http:// ...
- Redis并发锁控制
为了防止用户在页面上重复点击或者同时发起多次请求,请求处理需要操作redis缓存,这个时候需要对并发边界进行并发锁控制,实现思路: 由于每个页面发起的请求带的token具备唯一性,可以将token作为 ...
- Redis学习笔记~Redis并发锁机制
回到目录 redis客户端驱动有很多,如ServiceStack.Redis,StackExchange.Redis等等,下面我使用ServiceStack.Redis为例,介绍一下在redis驱动中 ...
- ServiceStack.Redis常用操作 - 事务、并发锁_转
一.事务 使用IRedisClient执行事务示例: using (IRedisClient RClient = prcm.GetClient()) { RClient.Add("key&q ...
- ServiceStack.Redis常用操作 - 事务、并发锁
一.事务 使用IRedisClient执行事务示例: using (IRedisClient RClient = prcm.GetClient()) { RClient.Add("key&q ...
- golang并发编程
golang并发编程 引子 golang提供了goroutine快速实现并发编程,在实际环境中,如果goroutine中的代码要消耗大量资源时(CPU.内存.带宽等),我们就需要对程序限速,以防止go ...
- golang 互斥锁和读写锁
golang 互斥锁和读写锁 golang中sync包实现了两种锁Mutex(互斥锁)和RWMutex(读写锁),其中RWMutex是基于Mutex实现的,只读锁的实现使用类似引用计数器的功能. ty ...
- 马蜂窝搜索基于 Golang 并发代理的一次架构升级
搜索业务是马蜂窝流量分发的重要入口.很多用户在使用马蜂窝时,都会有目的性地主动搜索与自己旅行需求相关的各种信息,衣食住行,事无巨细,从而做出最符合需求的旅行决策. 因此在马蜂窝,搜索业务交互的下游模块 ...
随机推荐
- 如何探测浏览器是否开启js功能
<body> ... ... <script type="text/javascript"> <!-- document.write("He ...
- Telnet登入cisco router 1800
Login to Router and change to privileged modec:\>telnet 192.168.6.1Trying 192.168.6.1...Connected ...
- IDM百度云使用
2018-8-6 (idm百度云速度很慢) Tips:如果感觉图片较小,可以ctrl+鼠标滚轮放大网页 首先下载IDM绿色版. 解压后:右键以管理员权限运行进行绿化 最后,它会在所有支持的浏览器上安装 ...
- Android GMS无法通过网络定位
前言 欢迎大家我分享和推荐好用的代码段~~ 声明 欢迎转载.但请保留文章原始出处: CSDN:http://www.csdn.net ...
- jQuery -> 获取指定上下文中的DOM元素
jQuery函数的第二个參数能够指定DOM元素的搜索范围. 第二个參数可分为下面类型 DOM reference jQuery wrapper document 代码演示样例 <!DOCTYPE ...
- Nodejs 一个简单的后台实例
http://blog.csdn.net/u014595019/article/details/50845726
- eclipse 开发jsp 智能提示设置
1.打开eclipse→Windows→Preferences→Java→Editor→Content Assist 改动Auto Activation triggers for java的值为:.a ...
- Android性能測试 一些适用于Android Studio的代码审查和性能測试工具
导言: Android应用在CPU占用,内存消耗方面的性能指标是影响产品质量的重要因素,因为QQ管家,360手机助手等应用都提供直观的内存消耗,流量监控功能,致使用户比以往更加关注软件的性能,并以此进 ...
- Massive Data Mining学习记录
第一周: 学习PageRank, 知识点:每个节点的权值由其他节点的投票决定,所有节点的权值和为1 当节点很多时候必须转换成矩阵运算来计算节点的最终值,由马尔可夫链可以证明,这个值可以迭代得到 问题: ...
- bzoj3196 二逼平衡树——线段树套平衡树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3196 人生中第一棵树套树! 写了一个晚上,成功卡时 9000ms+ 过了! 很要注意数组的大 ...