golang问题总结
//author: ysqi,https://yushuangqi.com package main import ( "fmt" ) func sliceModify(slice []int) { // slice[0] = 88 slice[0] = 1000 slice = append(slice,6) } func modify(array []int) { array[0] = 10 fmt.Println("In modify(),array values:",array) } func main() { slice := []int{1,2,3,4,5} sliceModify(slice) fmt.Println(slice) array := []int{1,5} modify(array) fmt.Println("In main(),array) } [1000 2 3 4 5] In modify(),array values: [10 2 3 4 5] In main(),array values: [10 2 3 4 5] go的函数传递除了map,slice,channel都是值类型传递,特别是数组不是和c一样是引用传递 其次slice的append操作如果超过了cap容量,就会生成新的slice package main import ( "fmt" "reflect" "unsafe" ) func main() { var t = make([]int,10) var s = make([]int,10) // 到此为止,t,s 都分配了底层数组,cap为10,只是t,s指定了len为0,fmt.Print时才没显示出内容来 fmt.Printf("addr:%p ttlen:%v content:%vn",len(t),t) // 这里的%p打印的其实是slice底层数组的首地址 fmt.Printf("addr:%p ttlen:%v content:%vn",s,len(s),s) t = append(s,1,4) // s的底层数组变化,append返回新的描述struct,假设是tmp,tmp的len则为4,并指向了s的底层数组,再用tmp覆盖t,所以下面两行fmt.Printf打印的%p一样 fmt.Println(t) fmt.Println(s) fmt.Printf("addr:%p ttlen:%v content:%vn",t) // t的len为4 fmt.Printf("addr:%p ttlen:%v content:%vn",s) // s的len为0,因为t,s的len不一样,内容才不同 fmt.Println("---- 辅助代码 -----") sliceHeaderT := (*reflect.SliceHeader)((unsafe.Pointer(&t))) sliceHeaderS := (*reflect.SliceHeader)((unsafe.Pointer(&s))) fmt.Printf("sliceHeaderT: %+vn",sliceHeaderT) // Data字段的值其实和上面你打印的地址是同一个,自己可以去换算一下 fmt.Printf("sliceHeaderS: %+vn",sliceHeaderS) fmt.Printf("addr:%p ttlen:%v content:%vn",&t,t) // t的真实地址,明显和你上面打印的不同 fmt.Printf("addr:%p ttlen:%v content:%vn",&s,s) s = append(s,5) // 修改s的底层数组,且len变为1 fmt.Println(t) // 因为t,s 共享底层数组,所以t,s的首个元素都是5 fmt.Println(s) fmt.Printf("sliceHeaderT: %+vn",sliceHeaderT) fmt.Printf("sliceHeaderS: %+vn",sliceHeaderS) sliceHeaderS.Len = 2 fmt.Println(s) }addr:0xc420064000 len:0 content:[] addr:0xc420064050 len:0 content:[] [1 2 3 4] [] addr:0xc420064050 len:4 content:[1 2 3 4] addr:0xc420064050 len:0 content:[] ---- 辅助代码 ----- sliceHeaderT: &{Data:842350870608 Len:4 Cap:10} sliceHeaderS: &{Data:842350870608 Len:0 Cap:10} addr:0xc42000a060 len:4 content:[1 2 3 4] addr:0xc42000a080 len:0 content:[] [5 2 3 4] [5] sliceHeaderT: &{Data:842350870608 Len:4 Cap:10} sliceHeaderS: &{Data:842350870608 Len:1 Cap:10} [5 2] ############################################################################################## 2 golang的闭包和匿名函数 func f(i int) func() int { return func() int { i++ return i } } 和js的闭包如出一辙,但是js的闭包原理没有研究过,golang的源码比较容易分析,但是应该是大差不差 首先要引用局部变量,该变量一定不能在堆栈上,必须分配到堆上才不至于释放,所以, go 会生成对应的函数对象类型,大概这样 执行的时候将函数对象也传给 fp 指向的函数 (比如通过寄存器) 3 golang多进程 1 golang没有如同c函数的fork函数,其多进程实现方案一般有两种, cmd := exec.Command(os.Args[0],args...) process,err := os.StartProcess(argv0,os.Args,&os.ProcAttr{ Dir: originalWD,Env: env,Files: allFiles,}) 其中看过平滑启动的一些代码注意到fork+execv方式派生子进程方式 又重新看了下nginx源码,nginx的平滑启动也采用该方式 c代码的fork+exevc派生的子进程会继承父进程打开的socket句柄 但是golang派生子进程需要通过传递属性才会继承所以有了下面代码 env = append(env,fmt.Sprintf("%s%d",envCountKeyPrefix,len(listeners))) allFiles := append([]*os.File{os.Stdin,os.Stdout,os.Stderr},files...) process,}) 其中files是打开的文件句柄 golang默认不传递,但是c的镜像进程方式会传递, 这里面牵扯一个问题, close_on_exec 是一个进程所有文件描述符(文件句柄)的位图标志,每个比特位代表一个打开的文件描述符,用于确定在调用系统调用execve()时需要关闭的文件句柄(参见include/fcntl.h)。当一个程序使用fork()函数创建了一个子进程时, 通常会在该子进程中调用execve()函数加载执行另一个新程序。此时子进程将完全被新程序替换掉,并在子进程中开始执行新程序。 若一个文件描述符在close_on_exec中的对应比特位被设置,那么在执行execve()时该描述符将被关闭,否则该描述符将始终处于打开状态。 所以默认excev会保持打开,如果用此标志位会默认关闭 通过以上发现,真的是环环相扣有点意思5
go test -v -bench . -benchmem
go test -race 竞争检测go build -gcflags=-m -o test 内联打印 go tool objdump -s "main.main" test 汇编打印 go tool pprof mysql.test cpu.prof 火焰图 /usr/local/Cellar/go/1.8.1/libexec/bin/go build -o wine -gcflags "-N -l -m" && GODEBUG="gctrace=1,scheddetail=1,schedtrace=1000" ./wine打印垃圾回收以及响应时间等 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |