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 变量,所以只是逃逸的成本不一样了而已。
rl, _ := rotatelogs.New("/path/to/access_log.%Y%m%d%H%M")
logrus.SetOutput(rl)
通过 logrus.SetOutput
覆盖写的句柄
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》
一下引用其中的一段介绍:
>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 语句的内部实现-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 的内存空间中的
一下引用其中的一段介绍:
>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 带来更大的压力
我一直自己抓取了一份每日新闻的数据, 我不知道这样是否好,但是我分享连接给大家,希望可以帮助大家梳理每日新闻的文章