zoukankan      html  css  js  c++  java
  • golang 记一次map中struct的管道造成死锁的解决方式

    一 死锁实例

    type Person struct {
        name  string
        ch chan string
    }
    
    var wg sync.WaitGroup
    
    func main(){
        p1 := Person{name: "wbw", }
        p2 := Person{name: "asa",}
        themap := make(map[string]Person)
        themap["p1"] = p1
        themap["p2"] = p2
    
        for _,ps := range themap{
            ps.ch = make(chan string)
    
            wg.Add(1)
            go getinfo(ps)
        }
    
        for i := 0; i < 10; i++ {
            time.Sleep(1 * time.Second)
            themap["p1"].ch <- "a"
            themap["p2"].ch <- "b"
        }
    
        for _,ps := range themap{
            close(ps.ch)
        }
        wg.Wait()
    
    }
    
    func getinfo(ps Person){
        for info := range ps.ch{
            fmt.Println(ps.name,info)
        }
        wg.Done()
    }

    二 问题分析

      2.1 map中struct不能修改

    结构体作为map的元素时,不能够直接赋值给结构体的某个字段,也就是map中的struct中的字段不能够直接寻址。

      这个现象的解释

    关于golang中map的这种古怪的特性有这样几个观点:

    1)map作为一个封装好的数据结构,由于它底层可能会由于数据扩张而进行迁移,所以拒绝直接寻址,避免产生野指针;

    2)map中的key在不存在的时候,赋值语句其实会进行新的k-v值的插入,所以拒绝直接寻址结构体内的字段,以防结构体不存在的时候可能造成的错误;

    3)这可能和map的并发不安全性相关

    • x = y 这种赋值的方式,你必须知道 x的地址,然后才能把值 y 赋给 x。
    • 但 go 中的 map 的 value 本身是不可寻址的,因为 map 的扩容的时候,可能要做 key/val pair迁移
    • value 本身地址是会改变的
    • 不支持寻址的话又怎么能赋值呢

      那么我们不管解释通不通,现象确实存在,本人也写过脚本验证过

      2.2 map中struct不能修改解决方式

    •  先用temp获取map中的struct,修改temp,再讲map中该struct替换成temp
       ---> 这种无法解决我们死锁问题,因为temp中的管道写消息,我们再range该map的时候,新的temp读消息,显然已经不是同一个管道,遂放弃
    •  指针,把map的value部分定义为对应类型的指针类型或是slice或是map时,这样是可以更新v的内部字段的
       ---> 此方案可解决,我们只需要让map中person结构体和p1 p2指向同一个指针即可,接下来提供解决实例

    三 debug实例

    type Person struct {
        name  string
        ch chan string
    }
    
    var wg sync.WaitGroup
    
    func main(){
        p1 := Person{name: "wbw", }
        p2 := Person{name: "asa",}
        themap := make(map[string]*Person)
        themap["p1"] = &p1
        themap["p2"] = &p2
    
        for _,ps := range themap{
            ps.ch = make(chan string)
    
            wg.Add(1)
            go getinfo(ps)
        }
    
        for i := 0; i < 10; i++ {
            time.Sleep(1 * time.Second)
            themap["p1"].ch <- "a"
            themap["p2"].ch <- "b"
        }
    
        for _,ps := range themap{
            close(ps.ch)
        }
        wg.Wait()
    
    }
    
    func getinfo(ps *Person){
        for info := range ps.ch{
            fmt.Println(ps.name,info)
        }
        wg.Done()
    }

  • 相关阅读:
    SQL Server数据库中批量替换数据的方法
    js 鼠标移上去弹出层效果
    50个jQuery代码段帮你成为更好的JavaScript开发者
    MSN、腾讯QQ、SKYPE、阿里旺旺网页在线客服源代码
    正则表达式基础知识
    常用邮箱POP3和SMTP服务器汇总
    CS5序列号
    如何使用Oracle SQLDeveloper 中连接MS SQLServer和MySQL数据库
    Silverlight与后台数据库的三种技术实现基本的互操作(转)
    ADO 与ADO.NET
  • 原文地址:https://www.cnblogs.com/bushuwei/p/15186633.html
Copyright © 2011-2022 走看看