基本信息
文件名称:Go语言中常见的坑以及高性能编程技巧分享.docx
文件大小:20.86 KB
总页数:11 页
更新时间:2025-05-22
总字数:约6.57千字
文档摘要

Go语言中常见的坑以及高性能编程技巧分享

目录背景本文相关代码的验证环境一.常见的坑1.1数据结构1.2Go语言特性相关二.高性能Go编程2.1数据结构2.2内存管理2.3并发编程

背景

代码的稳健性、高性能、可读性是我们每一位coder必须去追求的目标,也是coding的基本功。

本文结合Go语言的特性,以及自己在写Go项目中做的总结,从Go常见的数据结构、内存管理、并发等方面做了相关总结

本文相关代码的验证环境

GOARCH=arm64GOOS=darwinGOVERSION=go1.16.15

一.常见的坑

1.1数据结构

1.在函数调用过程中,数组是值传递pass-by-value,必要时必须使用slice。因为slice是引用类型,在slice传参时,只会复制slice的Data指针和len、cap,形参和实参的slice使用的是同一个底层数组。

2.map是一种hash表实现,每次遍历的顺序都可能不一样。

3.切片会导致整个数组被锁定,导致底层数组无法及时释放内存,如果底层数组过大,会对内存产生极大的压力。

错误示例:

functest(){

fileHeaderMap:=make(map[string][]byte)

fori:=0;ii++{

name:=/data/test/file_+strconv.Itoa(i)

data,err:=ioutil.ReadFile(name)

iferr!=nil{

fmt.Println(err)

continue

fileHeaderMap[name]=data[:1]

//dosomething

}

正确示例:

解决办法是将结果克隆一份,这样可以释放底层数组

functest(){

fileHeaderMap:=make(map[string][]byte)

fori:=0;ii++{

name:=/data/test/file_+strconv.Itoa(i)

data,err:=ioutil.ReadFile(name)

iferr!=nil{

fmt.Println(err)

continue

fileHeaderMap[name]=append([]byte{},data[:1]...)

//dosomething

}

4.当函数的可变参数是空接口时,传入空接口的切片时需要注意参数展开的问题

funcmain(){

vara=[]interface{}{111,222,333}

fmt.Println(a)

fmt.Println(a...)

//不管是否展开,编译器都会编译通过,但是输出是不同的:

//print:

[111222333]

111222333

5.对于切片的操作,会操作同一个底层数组,因此对于一个切片的修改操作,会影响到整个数组。另外由于string在go中是immutable,对于同一个字符串的两个变量,Go做了内存优化,会使用的相同的底层数组

funcmain(){

slice:=[]int{1,2,3,4,5}

slice1:=slice[:2]

slice2:=slice[:4]

fmt.Println(slice:,slice,,slice1:,slice1,,slice2:,slice2)

slice2[0]=6

fmt.Println(slice:,slice,,slice1:,slice1,,slice2:,slice2)

string1:=gohelloword

string2:=gohelloword

fmt.Printf(string1dataaddr:%d\n,(*reflect.StringHeader)(unsafe.Pointer(string1)).Data)

fmt.Printf(string2dataaddr:%d\n,(*reflect.StringHeader)(unsafe.Pointer(string2)).Data)

//print:

slice:[12345],slice1:[12],slice2:[1234]

slice:[