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

原文地址:https://www.cnblogs.com/bushuwei/p/15186633.html