• 循环内临时变量问题 at 2019年07月08日

    temp 可以安全的使用,没有做任何特殊的处理,可以理解为 temp 每次都是一个新的变量,编译器会根据你如何使用 temp 来决定是分配到堆上还是栈上的。

    fmt.Printf 调用之后 temp 地址就会不一样,println 调用就会一样。

    前文说的:

    package main
    
    import (
        "fmt"
        "unsafe"
    )
    
    func main() {
        for i := 0; i < 100; i++ {
            temp := i
            p := uintptr(unsafe.Pointer(&temp))
            fmt.Printf("address of temp:%x\n", p)
        }
    }
    

    这种避免 temp 逃逸,实际在编译过程已经处理好 uintptr(unsafe.Pointer(&temp)), 逃逸的对象不再是 temp 而是 p 变量,所以只是逃逸的成本不一样了而已。

  • logrus打印日志问题 at 2019年07月01日
    rl, _ := rotatelogs.New("/path/to/access_log.%Y%m%d%H%M")
    
    logrus.SetOutput(rl)
    
    

    通过 logrus.SetOutput 覆盖写的句柄

  • 关于递归和切片的问题 at 2019年03月01日
    package main
    
    import (
        "fmt"
        "reflect"
        "unsafe"
    )
    
    func main() {
        var val []int = make([]int, 0, 100)
        deepNum(3, val)
        for _, v := range val {
            fmt.Println(v)
        }
    
        s1 := (*reflect.SliceHeader)(unsafe.Pointer(&val))
        fmt.Println(s1)
        s1.Len = 5
    
        for _, v := range val {
            fmt.Println(v)
        }
    }
    
    func deepNum(deep int, ret []int) {
        if deep > 0 {
            deepNum(deep-1, ret)
        } else {
            fmt.Println("here")
            ret = append(ret, 100)
        }
    }
    

    上面代码的运行结果输出如下:

    here
    &{824634384384 0 100}
    100
    0
    0
    0
    0
    

    val 对应底层数组已经发生改变了,只是 val 的长度还是 0,所以你打印的时候是没有任何信息的 你需要了解 slice 实际是浅拷贝, 推荐阅读 《【Go】深入剖析 slice 和 array》

  • Go指针复制问题 at 2019年03月01日

    推荐看 《golang for 语句完全指南》

    一下引用其中的一段介绍:

    >for 语句的内部实现-array golang 的 for 语句,对于不同的格式会被编译器编译成不同的形式,如果要弄明白需要看 golang 的编译器和相关数据结构的源码, 数据结构源码还好,但是编译器是用 C++ 写的,本人 C++ 是个弱鸡,这里只讲 array 内部实现。

    // The loop we generate:
    //   len_temp := len(range)
    //   range_temp := range
    //   for index_temp = 0; index_temp < len_temp; index_temp++ {
    //           value_temp = range_temp[index_temp]
    //           index = index_temp
    //           value = value_temp
    //           original body
    //   }
    
    // 例如代码:  
    array := [2]int{1,2}
    for k,v := range array {
        f(k,v)
    }
    
    // 会被编译成:  
    len_temp := len(array)
    range_temp := array
    for index_temp = 0; index_temp < len_temp; index_temp++ {
        value_temp = range_temp[index_temp]
        k = index_temp
        v = value_temp
        f(k,v)
    }
    

    > 所以像遍历一个数组,最后生成的代码很像 C 语言中的遍历,而且有两个临时变量 index_temp,value_temp, 在整个遍历中一直复用这两个变量。所以会导致开头问题 2 的问题(详细解答会在后边)。

  • for 和 for range有什么区别? at 2019年03月01日

    推荐看 《golang for 语句完全指南》

    一下引用其中的一段介绍:

    >for 语句的内部实现-array golang 的 for 语句,对于不同的格式会被编译器编译成不同的形式,如果要弄明白需要看 golang 的编译器和相关数据结构的源码, 数据结构源码还好,但是编译器是用 C++ 写的,本人 C++ 是个弱鸡,这里只讲 array 内部实现。

    // The loop we generate:
    //   len_temp := len(range)
    //   range_temp := range
    //   for index_temp = 0; index_temp < len_temp; index_temp++ {
    //           value_temp = range_temp[index_temp]
    //           index = index_temp
    //           value = value_temp
    //           original body
    //   }
    
    // 例如代码:  
    array := [2]int{1,2}
    for k,v := range array {
        f(k,v)
    }
    
    // 会被编译成:  
    len_temp := len(array)
    range_temp := array
    for index_temp = 0; index_temp < len_temp; index_temp++ {
        value_temp = range_temp[index_temp]
        k = index_temp
        v = value_temp
        f(k,v)
    }
    

    > 所以像遍历一个数组,最后生成的代码很像 C 语言中的遍历,而且有两个临时变量 index_temp,value_temp, 在整个遍历中一直复用这两个变量。所以会导致开头问题 2 的问题(详细解答会在后边)。

  • for range 时,实际是遍历一个浅拷贝的 slice, 其 v 是一个临时变量, 这个临时变量是 slice 元素的拷贝, 在遍历时并不总是分配内存, 而是一直复用 v 的内存空间, 直接从内存拷贝数据到 v 的内存空间中的

    推荐看 《golang for 语句完全指南》

    一下引用其中的一段介绍:

    >for 语句的内部实现-array golang 的 for 语句,对于不同的格式会被编译器编译成不同的形式,如果要弄明白需要看 golang 的编译器和相关数据结构的源码, 数据结构源码还好,但是编译器是用 C++ 写的,本人 C++ 是个弱鸡,这里只讲 array 内部实现。

    // The loop we generate:
    //   len_temp := len(range)
    //   range_temp := range
    //   for index_temp = 0; index_temp < len_temp; index_temp++ {
    //           value_temp = range_temp[index_temp]
    //           index = index_temp
    //           value = value_temp
    //           original body
    //   }
    
    // 例如代码:  
    array := [2]int{1,2}
    for k,v := range array {
        f(k,v)
    }
    
    // 会被编译成:  
    len_temp := len(array)
    range_temp := array
    for index_temp = 0; index_temp < len_temp; index_temp++ {
        value_temp = range_temp[index_temp]
        k = index_temp
        v = value_temp
        f(k,v)
    }
    

    > 所以像遍历一个数组,最后生成的代码很像 C 语言中的遍历,而且有两个临时变量 index_temp,value_temp, 在整个遍历中一直复用这两个变量。所以会导致开头问题 2 的问题(详细解答会在后边)。

  • 想简单回答这个问题只能告诉你语言的规则,推荐看 《golang for 语句完全指南》

    一下引用其中的一段介绍:

    >for 语句的内部实现-array golang 的 for 语句,对于不同的格式会被编译器编译成不同的形式,如果要弄明白需要看 golang 的编译器和相关数据结构的源码, 数据结构源码还好,但是编译器是用 C++ 写的,本人 C++ 是个弱鸡,这里只讲 array 内部实现。

    // The loop we generate:
    //   len_temp := len(range)
    //   range_temp := range
    //   for index_temp = 0; index_temp < len_temp; index_temp++ {
    //           value_temp = range_temp[index_temp]
    //           index = index_temp
    //           value = value_temp
    //           original body
    //   }
    
    // 例如代码:  
    array := [2]int{1,2}
    for k,v := range array {
        f(k,v)
    }
    
    // 会被编译成:  
    len_temp := len(array)
    range_temp := array
    for index_temp = 0; index_temp < len_temp; index_temp++ {
        value_temp = range_temp[index_temp]
        k = index_temp
        v = value_temp
        f(k,v)
    }
    

    > 所以像遍历一个数组,最后生成的代码很像 C 语言中的遍历,而且有两个临时变量 index_temp,value_temp, 在整个遍历中一直复用这两个变量。所以会导致开头问题 2 的问题(详细解答会在后边)。

  • 引用一段 《string 优化误区及建议》 我认为这篇文章可以解答你的疑惑

    >【Go】string 优化误区及建议 原文链接: https://blog.thinkeridea.com/201902/go/string_ye_shi_yin_yong_lei_xing.html

    >本文原标题为 《string 也是引用类型》,经过 郝林 大佬指点原标题存在诱导性,这里解释一下 "引用类型" 有两个特征:1、多个变量引用一块内存数据,不创建变量的副本,2、修改任意变量的数据,其它变量可见。显然字符串只满足了 "引用类型" 的第一个特点,不能满足第二个特点,顾不能说字符串是引用类型,感谢大佬指正。

    >初学 Go 语言的朋友总会在传 [] byte 和 string 之间有着很多纠结,实际上是没有了解 string 与 slice 的本质,而且读了一些程序源码,也发现很多与之相关的问题,下面类似的代码估计很多初学者都写过,也充分说明了作者当时内心的纠结:

  • Duration 是持续时间,samples 貌似是程序运行时间的总和,如果包含 goroutine 那么就会有时间重合,没有验证,请自行验证

  • package main
    
    import (
        "encoding/json"
        "fmt"
        "io/ioutil"
        "net/http"
        "os"
        "time"
    
        "github.com/jeremywohl/flatten"
        "github.com/thinkeridea/go-extend/exbytes"
        "github.com/thinkeridea/go-extend/exstrings"
    )
    
    type item struct {
        Endpoint    string `json:"endpoint"`
        Metric      string `json:"metric"`
        Timestamp   int64  `json:"timestamp"`
        Step        int    `json:"step"`
        Value       int64  `json:"value"`
        CounterType string `json:"counterType"`
        Tags        string `json:"tags"`
    }
    
    type message struct {
        Item []item `json:"item"`
    }
    
    func getItem(m string, value int64) *message {
        var v item
        hostName, _ := os.Hostname()
        v.Endpoint = hostName
        v.Timestamp = time.Now().Unix()
        v.Step = 60
        v.CounterType = "counter"
        v.Tags = "service=get_stats"
        //v.Item = append(v.Item, item{Endpoint: hostName, Metric: m, Timestamp: time.Now().Unix(), Step: 10, Value: value})
        var o message
        o.Item = append(o.Item, v)
        return &o
    }
    
    func getMetric(url string, ch chan<- string) {
        resp, err := http.Get(url)
        if err != nil || resp.StatusCode != 200 {
            ch <- ""
            return
        }
        body, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            ch <- ""
            return
        }
        b := exbytes.ToString(body)
        flat, err := flatten.FlattenString(b, "", flatten.DotStyle)
        if err == nil {
            ch <- flat
            return
        }
    }
    
    type converge struct {
        Sum float64
        Num int
    }
    
    func main() {
        ch := make(chan string)
        for _, i := range []int{9010, 9011} {
            go getMetric(fmt.Sprintf("http://127.0.0.1:%d/stats", i), ch)
        }
    
        converges := map[string]*converge{}
        for i := 0; i < 2; i++ {
    
            r := <-ch
            if r == "" {
                continue
            }
    
            data := make(map[string]*float64, 100)
            err := json.Unmarshal(exstrings.UnsafeToBytes(r), &data)
            if err != nil {
                panic(err)
            }
    
            for k, v := range data {
                if _, ok := converges[k]; !ok {
                    converges[k] = &converge{}
                }
    
                converges[k].Num++
                if v != nil {
                    converges[k].Sum += *v
                }
            }
        }
    
        avg := map[string]float64{}
        for k, v := range converges {
            avg[k] = v.Sum / float64(v.Num)
        }
    
        fmt.Println(avg)
    }
    
    

    简略的实现,并不是最优的代码,不建议使用 chan *string, 字符串传递的代价非常的小,使用指针反而会给 GC 带来更大的压力