简介

本篇文章的主要内容是解决go语言map在使用中遇到的两个问题,对于初学者是不可避免的坑

一、cannot assign to struct field

当map中存在struct类型的成员,如果在初始化后,再次对其进行修改,就会出现这种错误。

type student struct {
name string
age int
} var testMap = map[string]student{
"xiao":{"xiao", },
"long":{"long", },
"ren": {"ren" , },
} func main(){
testMap["xiao"].name = "da"
}

以上执行过程,即我们对于元素修改的惯用思路,直接访问数据项,对于其进行修改。但在go build的过程中,会报错:cannot assign to struct field。

因为map并非是一个并发安全的结构,因此不可以直接修改其中涉及到struct的相关性。难道map初始化就无法修改了吗?答案是否定,只是修改的方式比较特殊。

对于上述问题的有两种解决方案

1、不修改原有map的结构,达到修改数据项的目的。

如果想要修改map中struct的某一项的内容,就要修改整个struct的内容,重新赋予key的value。

func main() {
testMap["xiao"] = student{
name: "da",
age: testMap["xiao"].age,
}
}

对于其中不需要改变的数据项采用原始数据,对于需要修改的数据项采用新数据。

2、修改原有map的结构,达到修改数据项的目的。

将map定义中的struct项设定为地址的形式。

var testMap = map[string] *student{
"xiao": {"xiao", },
"long": {"long", },
"ren": {"ren", },
}

将其改为指针的形式,map就可以直接修改struct中数据项了。

func main() {
testMap["xiao"].name = "da"
}

此时go build进行编译就不会报任何错了。建议采用第2种解决方案,符合日常的操作习惯。

二、fatal error:concurrent map read and map write

如果map由多goroutine同时进行读写操作,就会出现fatal error:concurrent map read and map write错误。

因为map并不像chan,对于goroutine的同步访问是安全的,map为引用类型,即使是函数调用,也不会产生多个副本,因此对于多个goroutine的访问,实际上是对同一块内存进行访问。基于我们对临界资源的认识,如果不加任何限制的对map进行访问,map共享资源就会遭到破坏并报错,这种错误也不是固定的,而是随机的,因为并不是每次对map的操作都会引起这种错误。

针对上述错误,一般有如下两种解决方案。

1、加锁

go语言sync包中实现了两种锁Mutex(互斥锁)和RWMutex(读写锁),其中RWMutex是基于Mutex实现的。

(1)互斥锁

type Mutex
func (m *Mutex) Lock()
func (m *Mutex) Unlock()

其中Lock()加锁,Unlock解锁,成对进行使用,否则会panic。使用Lock()加锁后,便不能再次对其进行加锁,直到利用Unlock解锁。对于使用读写锁的资源,每次只能有一个goroutine对其进行访问,适用于读写操作没有明显区别的场景。

对map使用互斥锁举例。

type Demo struct {
Data map[string]string
Lock sync.Mutex
} func (d Demo) Get(k string) string{
d.Lock.Lock()
defer d.Lock.Unlock()
return d.Data[k]
} func (d Demo) Set(k,v string) {
d.Lock.Lock()
defer d.Lock.Unlock()
d.Data[k]=v
}

2、读写锁

type RWMutex
func (rw *RWMutex) Lock()
func (rw *RWMutex) RLock()
func (rw *RWMutex) RLocker() Locker
func (rw *RWMutex) RUnlock()
func (rw *RWMutex) Unlock()

RWMutex为读写锁,该锁可以对某个资源加多个读锁或者一个写锁,适用于读次数远大于写次数的场景。

注(1)其中写锁RLock()的优先级要高于读锁Lock(),当有写锁请求时,读请求就阻塞,直到没有写锁或者没有锁时,才会加载读锁。

(2)读写锁都是成对使用的,并且加锁要在解锁前使用,否则会panic或者fatal error。

对map使用读写锁举例。

type Demo struct {
Data map[string]string
Lock sync.RWMutex
} func (d Demo) Get(k string) string{
d.Lock.RLock()
defer d.Lock.RUnlock()
return d.Data[k]
} func (d Demo) Set(k,v string) {
d.Lock.Lock()
defer d.Lock.Unlock()
d.Data[k]=v
}

2、利用channel串行化处理

能使用chan的场景,推荐使用chan进行goroutine的交互。chan自身的机制,保证数据访问的高效性和正确性。

参考链接:

https://blog.csdn.net/skh2015java/article/details/60334091

https://haobook.readthedocs.io/zh_CN/latest/periodical/201611/zhangan.html

https://blog.csdn.net/skh2015java/article/details/60334437

【Go语言】map在goroutine通信中的使用问题的更多相关文章

  1. golang(8):channel读写 & goroutine 通信

    goroutine 1.进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位 B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独 ...

  2. Go语言Map的使用

    Go 语言Map(集合) Map 是一种无序的键值对的集合.Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值. Map 是一种集合,所以我们可以像迭代数组和切片那样 ...

  3. go语言---map

    go语言---map https://blog.csdn.net/cyk2396/article/details/78890185 一.map的用法: type PersonDB struct { I ...

  4. Android IOS WebRTC 音视频开发总结(七五)-- WebRTC视频通信中的错误恢复机制

    本文主要介绍WebRTC视频通信中的错误恢复机制(我们翻译和整理的,译者:jiangpeng),最早发表在[这里] 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:blac ...

  5. 使用SSL确保通信中的数据安全

    #region Server /// <summary> /// 用于保存非对称加密(数字证书)的公钥 /// </summary> private string public ...

  6. iOS: 消息通信中的Notification&KVO

    iOS: 消息通信中的Notification&KVO 在 iOS: MVC 中,我贴了张经典图: 其中的Model向Controller通信的Noification&KVO为何物呢? ...

  7. TCP通信中的大文件传送

    TCP通信中的大文件传送 源码   (为节省空间,不包含通信框架源码,通信框架源码请另行下载) 文件传送在TCP通信中是经常用到的,本文针对文件传送进行探讨 经过测试,可以发送比较大的文件,比如1个G ...

  8. 串口通信中ReadFile和WriteFile的超时详解!

    源:串口通信中ReadFile和WriteFile的超时详解! 在用ReadFile和WriteFile读写串行口时,需要考虑超时问题.如果在指定的时间内没有读出或写入指定数量的字符,那么ReadFi ...

  9. 高效率遍历Map以及在循环过程中移除 remove指定key

    //高效率遍历Map以及在循环过程中移除 remove指定key //使用iter循环的时候 可以在循环中移除key,for在循环的过程中移除会报错哦 //本方法效率高 Iterator iter = ...

随机推荐

  1. Java 缓存池(使用Map实现)

    之前只是听说过缓存池,也没有具体的接触到,今天做项目忽然想到了用缓存池,就花了一上午的时间研究了下缓存池的原理,并实现了基本的缓存池功能. /** * 缓存池 * @author xiaoquan * ...

  2. 【leetcode&CN&竞赛】1196.How Many Apples Can You Put into the Basket

    题目如下: 楼下水果店正在促销,你打算买些苹果,arr[i] 表示第 i 个苹果的单位重量. 你有一个购物袋,最多可以装 5000 单位重量的东西,算一算,最多可以往购物袋里装入多少苹果. 示例 1: ...

  3. delphi回调函数高级应用

    用法一 Type TProc=procedure() of object; procedure Callback(proc:TProc); begin ... end; procedure mypro ...

  4. 更改pip源地址为阿里云

    1.在用户名目录创建pip目录,在pip目录下创建pip.ini. 2.pip.ini中输入: [global] index-url = http://mirrors.aliyun.com/pypi/ ...

  5. netty-socketio(二)整合redis实现发布订阅

    1.Redis 发布订阅 参考:https://www.runoob.com/redis/redis-pub-sub.html Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub ...

  6. 【java工具类】下载文件

    FileUtil.java /** * 下载文件 * @param file; * @param response */ public static void downloadFile(File fi ...

  7. Navicat使用与python操作数据库

    一.Navicat使用 1.下载地址: <https://pan.baidu.com/s/1bpo5mqj> 2.测试+链接数据库,新建库 3.新建表,新增字段+类型+约束 4.设计表:外 ...

  8. asp.net ajax传递Json给aspx.cs后台 webmethod方法传递json

    1.提取一个向后台写入数据的方法 ///向后台cs页面请求数据的方法 function myPost(url,data,func) { $.ajax({ type: "post", ...

  9. [BZOJ1697][USACO2007 FEB]Cow Sorting牛排序:贪心+置换

    分析 一个月前做的一道题补一下题解,就简单写一写吧. 单独考虑每一个循环节,如果只进行内部的调整,最优方案显然是把最小的绕这个循环交换一圈. 但是借助全局最小值可能使答案更优,两种情况取个\(\max ...

  10. 大哥带的Orchel数据库时间盲注

    0X01Oracle基于延时的盲注总结 0x00 前言 oracle注入中可以通过页面响应的状态,这里指的是响应时间,通过这种方式判断SQL是否被执行的方式,便是时间盲注: oracle的时间盲注通常 ...