可变参数是空接口类型
当参数的可变参数是空接口类型时,传入空接口的切片时需要注意参数展开的问题。
func main() { var a = []interface{}{1, 2, 3} fmt.println(a)//传入切片类型 fmt.println(a...)//传入切片的值} 不管是否展开,编译器都无法发现错误,但是输出是不同的:
[1 2 3]1 2 3 数组是值传递
在函数调用参数中,数组是值传递,无法通过修改数组类型的参数返回结果。必要时需要使用切片。
func main() { x := [3]int{1, 2, 3} func(arr [3]int) { arr[0] = 7 fmt.println(arr) }(x) fmt.println(x)}map遍历是顺序不固定 map是一种hash表实现,每次遍历的顺序都可能不一样。
func main() {
m := map[string]string{ 1: 1, 2: 2, 3: 3, } for k, v := range m { println(k, v) }} 返回值被屏蔽
在局部作用域中,命名的返回值内同名的局部变量屏蔽:
func foo() (err error) {
if err := bar(); err != nil {//内部变量err与返回值err冲突了 return } return} recover必须在defer函数中运行
recover捕获的是祖父级调用时的异常,直接调用时无效:
func main() { recover() panic(1)} 直接defer调用也是无效:
func main() { defer recover() panic(1)} defer调用时多层嵌套依然无效:
func main() { defer func() { func() { recover() }() }() panic(1)} 必须在defer函数中直接调用才有效:
func main() { defer func() { recover() }() panic(1)}main函数提前退出 后台goroutine无法保证完成任务。
func main() { go println(hello)//main不会等待新协程,新协程可能被中断}独占cpu导致其它goroutine饿死 goroutine是协作式抢占调度,goroutine本身不会主动放弃cpu:
func main() { runtime.gomaxprocs(1) go func() { for i := 0; i < 10; i++ { fmt.println(i) } }() for {} // 占用cpu} 解决的方法是在for循环加入runtime.gosched()调度函数:
func main() { runtime.gomaxprocs(1) go func() { for i := 0; i < 10; i++ { fmt.println(i) } }() for { runtime.gosched() }} 或者是通过阻塞的方式避免cpu占用:
func main() { runtime.gomaxprocs(1) go func() { for i := 0; i < 10; i++ { fmt.println(i) } os.exit(0) }() select{}}不同goroutine之间不满足顺序一致性内存模型 因为在不同的goroutine,main函数中无法保证能打印出hello, world:
var msg stringvar done boolfunc setup() { msg = hello, world done = true//在main协程中看来,done语句不一定在msg之后完成}func main() { go setup() for !done { } println(msg)}解决的办法是用chan显式同步:var msg stringvar done = make(chan bool)func setup() { msg = hello, world done <- true}func main() { go setup() <-done println(msg)} msg的写入是在channel发送之前,所以能保证打印hello, world
defer错误引用同一个变量
func main() { for i := 0; i < 5; i++ { defer func() { println(i)//这里打印一直是5,不是4,3,2,1,0 }() }} 改进的方法是通过函数参数传入:
func main() { for i := 0; i < 5; i++ { defer func(i int) { println(i) }(i) }} 切片会导致整个底层数组被锁定
切片会导致整个底层数组被锁定,底层数组无法释放内存。如果底层数组较大会对内存产生很大的压力。
func main() { headermap := make(map[string][]byte) for i := 0; i < 5; i++ { name := /path/to/file data, err := ioutil.readfile(name) if err != nil { log.fatal(err) } //创建了data的切片,导致data不会被释放,文件全部内容一直在内存中 headermap[name] = data[:1] } // do some thing} 解决的方法是将结果克隆一份,这样可以释放底层的数组:
func main() { headermap := make(map[string][]byte) for i := 0; i < 5; i++ { name := /path/to/file data, err := ioutil.readfile(name) if err != nil { log.fatal(err) } headermap[name] = append([]byte{}, data[:1]...) } // do some thing}空指针和空接口不等价 比如返回了一个错误指针,但是并不是空的error接口:
func returnserror() error { var p *myerror = nil if bad() { p = errbad } return p // will always return a non-nil error.} 内存地址会变化
go语言中对象的地址可能发生变化,因此指针不能从其它非指针类型的值生成:
func main() { var x int = 42 var p uintptr = uintptr(unsafe.pointer(&x)) runtime.gc()//运行垃圾回收,内存地址会发送变化 var px *int = (*int)(unsafe.pointer(p)) println(*px)}当内存发送变化的时候,相关的指针会同步更新,但是非指针类型的uintptr不会做同步更新。同理cgo中也不能保存go对象地址。 goroutine泄露
go语言是带内存自动回收的特性,因此内存一般不会泄漏。但是goroutine确存在泄漏的情况,同时泄漏的goroutine引用的内存同样无法被回收。
func main() { ch := func() <-chan int { ch := make(chan int) go func() { for i := 0; ; i++ { ch <- i } } () return ch }() for v := range ch { fmt.println(v) if v == 5 { break//break之后,主协程不再读取chan,新协程会一直阻塞 } }} 上面的程序中后台goroutine向管道输入自然数序列,main函数中输出序列。但是当break跳出for循环的时候,后台goroutine就处于无法被回收的状态了。我们可以通过context包来避免这个问题:
差动保护原理
应用华为云大数据BI解决方案下的金融业现状
杰和8GPU服务器3个方面实测
电池储能系统市场预计从2018的19亿8000万美元增长到2023年的85亿4000万美元
RYJ线的作用是什么,RYJ低烟无卤线的型号有哪些
嵌入式Linux应用开发之开发坑记录
华为高价收购荣耀:不会再受到美国对华为的制裁
交流电路的基本特征量_三种基本的交流电路图及波形图
AI技术将持续在未来五年内创造庞大的商机与工作机会
基于多位LED显示及键盘管理器件实现实现智能仪表接口的设计
关于Wi-Fi CERTIFIED EasyMesh测试计划v4.1版本
水银温度计要测多久_水银温度计打碎了怎么处理
无源音箱怎么接线_无源音箱的功放怎么选
最大IPO!ARM上市暴涨!
虚拟现实将改变我们的生活,但它会伤害我们吗?
PWM与电压转换芯片:APC&PAC芯片
干货:汽车RF通用系统前端的设计方案
用于可穿戴自充电生物超级电容器的MXene双功能生物阳极设计
java语言B/S架构医院云HIS系统源码【springboot】
优信充分将VR技术应用于二手车行业,推出首个VR全景看车服务