zoukankan      html  css  js  c++  java
  • 【Go语言】map在goroutine通信中的使用问题

    简介

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

    一、cannot assign to struct field

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

    type student struct {
        name string
        age int
    }
    
    var testMap = map[string]student{
        "xiao":{"xiao", 18},
        "long":{"long", 19},
        "ren": {"ren" , 20},
    }
    
    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", 18},
        "long": {"long", 19},
        "ren":  {"ren", 20},
    }

    将其改为指针的形式,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

  • 相关阅读:
    win7-64系统下安装nodejs
    Vue项目使用npm run dev出现npm ERR! missing script: dev
    本地环境时覆盖Apollo配置
    金蝶K3序时簿页面增加物料即时库存显示功能
    LeetCode——开篇
    url 与 params 参数的常见操作归纳汇总(含精心准备的注释)
    如何让 arcgis require 里定义的方法可以在全局访问?
    字体图标库 iconfont、iconmoon 的维护管理与使用探索
    【转载】ES5-ES12 常用语法特性
    一次 outline 去除经验(非继承属性,看着像继承)
  • 原文地址:https://www.cnblogs.com/longcnblogs/p/9678393.html
Copyright © 2011-2022 走看看