错误代码示例

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 并发锁的陷阱的更多相关文章

  1. Golang - 并发编程

    目录 Golang - 并发编程 1. 并行和并发 2. go语言并发优势 3. goroutine是什么 4. 创建goroutine 5. runtime包 6. channel是什么 7. ch ...

  2. Redis修改数据多线程并发—Redis并发锁

    本文版权归博客园和作者本人吴双共同所有 .转载爬虫请注明地址,博客园蜗牛 http://www.cnblogs.com/tdws/p/5712835.html 蜗牛Redis系列文章目录http:// ...

  3. Redis并发锁控制

    为了防止用户在页面上重复点击或者同时发起多次请求,请求处理需要操作redis缓存,这个时候需要对并发边界进行并发锁控制,实现思路: 由于每个页面发起的请求带的token具备唯一性,可以将token作为 ...

  4. Redis学习笔记~Redis并发锁机制

    回到目录 redis客户端驱动有很多,如ServiceStack.Redis,StackExchange.Redis等等,下面我使用ServiceStack.Redis为例,介绍一下在redis驱动中 ...

  5. ServiceStack.Redis常用操作 - 事务、并发锁_转

    一.事务 使用IRedisClient执行事务示例: using (IRedisClient RClient = prcm.GetClient()) { RClient.Add("key&q ...

  6. ServiceStack.Redis常用操作 - 事务、并发锁

    一.事务 使用IRedisClient执行事务示例: using (IRedisClient RClient = prcm.GetClient()) { RClient.Add("key&q ...

  7. golang并发编程

    golang并发编程 引子 golang提供了goroutine快速实现并发编程,在实际环境中,如果goroutine中的代码要消耗大量资源时(CPU.内存.带宽等),我们就需要对程序限速,以防止go ...

  8. golang 互斥锁和读写锁

    golang 互斥锁和读写锁 golang中sync包实现了两种锁Mutex(互斥锁)和RWMutex(读写锁),其中RWMutex是基于Mutex实现的,只读锁的实现使用类似引用计数器的功能. ty ...

  9. 马蜂窝搜索基于 Golang 并发代理的一次架构升级

    搜索业务是马蜂窝流量分发的重要入口.很多用户在使用马蜂窝时,都会有目的性地主动搜索与自己旅行需求相关的各种信息,衣食住行,事无巨细,从而做出最符合需求的旅行决策. 因此在马蜂窝,搜索业务交互的下游模块 ...

随机推荐

  1. 如何探测浏览器是否开启js功能

    <body> ... ... <script type="text/javascript"> <!-- document.write("He ...

  2. Telnet登入cisco router 1800

    Login to Router and change to privileged modec:\>telnet 192.168.6.1Trying 192.168.6.1...Connected ...

  3. IDM百度云使用

    2018-8-6 (idm百度云速度很慢) Tips:如果感觉图片较小,可以ctrl+鼠标滚轮放大网页 首先下载IDM绿色版. 解压后:右键以管理员权限运行进行绿化 最后,它会在所有支持的浏览器上安装 ...

  4. Android GMS无法通过网络定位

    前言          欢迎大家我分享和推荐好用的代码段~~ 声明          欢迎转载.但请保留文章原始出处:          CSDN:http://www.csdn.net        ...

  5. jQuery -&gt; 获取指定上下文中的DOM元素

    jQuery函数的第二个參数能够指定DOM元素的搜索范围. 第二个參数可分为下面类型 DOM reference jQuery wrapper document 代码演示样例 <!DOCTYPE ...

  6. Nodejs 一个简单的后台实例

    http://blog.csdn.net/u014595019/article/details/50845726

  7. eclipse 开发jsp 智能提示设置

    1.打开eclipse→Windows→Preferences→Java→Editor→Content Assist 改动Auto Activation triggers for java的值为:.a ...

  8. Android性能測试 一些适用于Android Studio的代码审查和性能測试工具

    导言: Android应用在CPU占用,内存消耗方面的性能指标是影响产品质量的重要因素,因为QQ管家,360手机助手等应用都提供直观的内存消耗,流量监控功能,致使用户比以往更加关注软件的性能,并以此进 ...

  9. Massive Data Mining学习记录

    第一周: 学习PageRank, 知识点:每个节点的权值由其他节点的投票决定,所有节点的权值和为1 当节点很多时候必须转换成矩阵运算来计算节点的最终值,由马尔可夫链可以证明,这个值可以迭代得到 问题: ...

  10. bzoj3196 二逼平衡树——线段树套平衡树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3196 人生中第一棵树套树! 写了一个晚上,成功卡时 9000ms+ 过了! 很要注意数组的大 ...