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()
    }

  • 相关阅读:
    JSP -java service pages
    java去重(1通过迭代器,2直接赋值)
    android 设置TextView水平滚动和解决首行缩进问题
    java 中字符串比较equals()和equalsIgnoreCase()的区别
    cursor的moveToNext()与moveToFirst()
    android studio快捷键
    android Android SDK Manager遇到的问题
    android onSaveInstanceState应用实例
    ProgressDialog的使用及逻辑处理
    POJ 1986 Distance Queries 【输入YY && LCA(Tarjan离线)】
  • 原文地址:https://www.cnblogs.com/bushuwei/p/15186633.html
Copyright © 2011-2022 走看看