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 并发代理的一次架构升级
搜索业务是马蜂窝流量分发的重要入口.很多用户在使用马蜂窝时,都会有目的性地主动搜索与自己旅行需求相关的各种信息,衣食住行,事无巨细,从而做出最符合需求的旅行决策. 因此在马蜂窝,搜索业务交互的下游模块 ...
随机推荐
- 在虚拟机搭建JStrom
原文:http://blog.csdn.net/u014134180/article/details/51810311 一 安装步骤 二 搭建Zookeeper集群 1 ZooKeeper 单机安装与 ...
- Guass列主元、平方根法、追赶法求解方程组的C++实现
一,要解决的问题 选用合适的算法,求解三种线性方程组:一般线性方程组,对称正定方程组,三对角线性方程组. 方程略. 二,数值方法 1,使用Guass列主元消去法求解一般线性方程组. Guass列主元是 ...
- C#中Stack<T>类的使用及部分成员函数的源代码分析
Stack<T>类 Stack<T> 作为数组来实现. Stack<T> 的容量是 Stack<T> 能够包括的元素数. 当向 Stack<T&g ...
- android application类简单介绍(一)
每次应用程序执行时.应用程序的application类保持实例化的状态. 通过扩展applicaiton类,能够完毕下面3项工作: 1.对android执行时广播的应用程序级事件如低低内做出响应. 2 ...
- [Python] How to unpack and pack collection in Python?
It is a pity that i can not add the video here. As a result, i offer the link as below: How to unpa ...
- Windows 驱动开发 - 7
在<Windows 驱动开发 - 5>我们所说的读写操作在本篇实现. 在WDF中实现此功能主要为:EvtIoRead和EvtIoWrite. 首先,在EvtDeviceAdd设置以上两个回 ...
- Java String常见问题
一.怎样推断两个String是否相等??使用"=="还是使用"equals()"? 对String来说."=="是用来推断两个字符串(对象) ...
- HDU 4445 数学-抛物运动
D - Crazy Tank ...
- POJ 1330 LCA裸题~
POJ 1330 Description A rooted tree is a well-known data structure in computer science and engineerin ...
- hdu 6035(树形dp)
题意:给你棵树,树上每个节点都有颜色,每条路径上有m种颜色 问你所有路径上出现的颜色的和 思路:答案求的是每种颜色对路径的贡献 我们可以反过来每种颜色不经过的路径的条数 假设根节点的颜色为x 我 ...